aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util/ca
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca')
-rw-r--r--src/ext/Util/ca/BroadcastSettingChange.cpp45
-rw-r--r--src/ext/Util/ca/CheckReboot.cpp36
-rw-r--r--src/ext/Util/ca/CloseApps.cpp568
-rw-r--r--src/ext/Util/ca/CustomMsiErrors.h32
-rw-r--r--src/ext/Util/ca/FormatFiles.cpp221
-rw-r--r--src/ext/Util/ca/OsInfo.cpp487
-rw-r--r--src/ext/Util/ca/RemoveFoldersEx.cpp243
-rw-r--r--src/ext/Util/ca/RemoveRegistryKeysEx.cpp114
-rw-r--r--src/ext/Util/ca/RestartManager.cpp185
-rw-r--r--src/ext/Util/ca/TouchFile.cpp308
-rw-r--r--src/ext/Util/ca/XmlConfig.cpp1130
-rw-r--r--src/ext/Util/ca/XmlFile.cpp940
-rw-r--r--src/ext/Util/ca/caDecor.h13
-rw-r--r--src/ext/Util/ca/cost.h9
-rw-r--r--src/ext/Util/ca/dllmain.cpp26
-rw-r--r--src/ext/Util/ca/exitearlywithsuccess.cpp27
-rw-r--r--src/ext/Util/ca/netshortcuts.cpp437
-rw-r--r--src/ext/Util/ca/precomp.h54
-rw-r--r--src/ext/Util/ca/qtexecca.cpp316
-rw-r--r--src/ext/Util/ca/sca.h19
-rw-r--r--src/ext/Util/ca/scacost.h18
-rw-r--r--src/ext/Util/ca/scaexec.cpp1082
-rw-r--r--src/ext/Util/ca/scamanifest.cpp377
-rw-r--r--src/ext/Util/ca/scaperf.cpp310
-rw-r--r--src/ext/Util/ca/scaperfexec.cpp423
-rw-r--r--src/ext/Util/ca/scasched.cpp127
-rw-r--r--src/ext/Util/ca/scasmb.h46
-rw-r--r--src/ext/Util/ca/scasmbexec.cpp316
-rw-r--r--src/ext/Util/ca/scasmbexec.h27
-rw-r--r--src/ext/Util/ca/scasmbsched.cpp639
-rw-r--r--src/ext/Util/ca/scauser.cpp709
-rw-r--r--src/ext/Util/ca/scauser.h67
-rw-r--r--src/ext/Util/ca/secureobj.cpp915
-rw-r--r--src/ext/Util/ca/serviceconfig.cpp821
-rw-r--r--src/ext/Util/ca/shellexecca.cpp271
-rw-r--r--src/ext/Util/ca/test.cpp269
-rw-r--r--src/ext/Util/ca/utilca.cpp3
-rw-r--r--src/ext/Util/ca/utilca.def91
-rw-r--r--src/ext/Util/ca/utilca.vcxproj106
39 files changed, 11827 insertions, 0 deletions
diff --git a/src/ext/Util/ca/BroadcastSettingChange.cpp b/src/ext/Util/ca/BroadcastSettingChange.cpp
new file mode 100644
index 00000000..2e153ad3
--- /dev/null
+++ b/src/ext/Util/ca/BroadcastSettingChange.cpp
@@ -0,0 +1,45 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/********************************************************************
7WixBroadcastSettingChange
8
9 Send WM_SETTINGCHANGE message to all top-level windows indicating
10 that unspecified settings have changed.
11********************************************************************/
12extern "C" UINT __stdcall WixBroadcastSettingChange(
13 __in MSIHANDLE hInstall
14 )
15{
16 HRESULT hr = WcaInitialize(hInstall, "WixBroadcastSettingChange");
17 ExitOnFailure(hr, "failed to initialize WixBroadcastSettingChange");
18
19 // best effort; ignore failures
20 ::SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, NULL, SMTO_ABORTIFHUNG, 1000, NULL);
21
22LExit:
23 return WcaFinalize(ERROR_SUCCESS);
24}
25
26
27/********************************************************************
28WixBroadcastEnvironmentChange
29
30 Send WM_SETTINGCHANGE message to all top-level windows indicating
31 that environment variables have changed.
32********************************************************************/
33extern "C" UINT __stdcall WixBroadcastEnvironmentChange(
34 __in MSIHANDLE hInstall
35 )
36{
37 HRESULT hr = WcaInitialize(hInstall, "WixBroadcastEnvironmentChange");
38 ExitOnFailure(hr, "failed to initialize WixBroadcastEnvironmentChange");
39
40 // best effort; ignore failures
41 ::SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, NULL, reinterpret_cast<LPARAM>(L"Environment"), SMTO_ABORTIFHUNG, 1000, NULL);
42
43LExit:
44 return WcaFinalize(ERROR_SUCCESS);
45}
diff --git a/src/ext/Util/ca/CheckReboot.cpp b/src/ext/Util/ca/CheckReboot.cpp
new file mode 100644
index 00000000..ce056411
--- /dev/null
+++ b/src/ext/Util/ca/CheckReboot.cpp
@@ -0,0 +1,36 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/********************************************************************
7WixCheckRebootRequired - entry point for WixCheckRebootRequired Custom Action
8
9 called as Type 1 CustomAction (binary DLL) from Windows Installer
10 in InstallExecuteSequence after InstallFinalize
11********************************************************************/
12extern "C" UINT __stdcall WixCheckRebootRequired(
13 __in MSIHANDLE hInstall
14 )
15{
16 HRESULT hr = S_OK;
17 DWORD er = ERROR_SUCCESS;
18
19 hr = WcaInitialize(hInstall, "WixCheckRebootRequired");
20 ExitOnFailure(hr, "failed to initialize");
21
22 if (WcaDidDeferredActionRequireReboot())
23 {
24 WcaLog(LOGMSG_STANDARD, "Reboot required by deferred CustomAction.");
25
26 er = ::MsiSetMode(hInstall, MSIRUNMODE_REBOOTATEND, TRUE);
27 hr = HRESULT_FROM_WIN32(er);
28 ExitOnFailure(hr, "Failed to schedule reboot.");
29 }
30
31LExit:
32
33 if (FAILED(hr))
34 er = ERROR_INSTALL_FAILURE;
35 return WcaFinalize(er);
36}
diff --git a/src/ext/Util/ca/CloseApps.cpp b/src/ext/Util/ca/CloseApps.cpp
new file mode 100644
index 00000000..d4256c43
--- /dev/null
+++ b/src/ext/Util/ca/CloseApps.cpp
@@ -0,0 +1,568 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define DEFAULT_PROCESS_EXIT_WAIT_TIME 5000
6
7// structs
8LPCWSTR wzQUERY_CLOSEAPPS = L"SELECT `Wix4CloseApplication`, `Target`, `Description`, `Condition`, `Attributes`, `Property`, `TerminateExitCode`, `Timeout` FROM `Wix4CloseApplication` ORDER BY `Sequence`";
9enum eQUERY_CLOSEAPPS { QCA_ID = 1, QCA_TARGET, QCA_DESCRIPTION, QCA_CONDITION, QCA_ATTRIBUTES, QCA_PROPERTY, QCA_TERMINATEEXITCODE, QCA_TIMEOUT };
10
11// CloseApplication.Attributes
12enum CLOSEAPP_ATTRIBUTES
13{
14 CLOSEAPP_ATTRIBUTE_NONE = 0x0,
15 CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE = 0x1,
16 CLOSEAPP_ATTRIBUTE_REBOOTPROMPT = 0x2,
17 CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE = 0x4,
18 CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE = 0x8,
19 CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE = 0x10,
20 CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS = 0x20,
21 CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE = 0x40,
22};
23
24struct PROCESS_AND_MESSAGE
25{
26 DWORD dwProcessId;
27 DWORD dwMessageId;
28 DWORD dwTimeout;
29};
30
31
32/******************************************************************
33 EnumWindowsProc - callback function which sends message if the
34 current window matches the passed in process ID
35
36******************************************************************/
37BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
38{
39 PROCESS_AND_MESSAGE* pPM = reinterpret_cast<PROCESS_AND_MESSAGE*>(lParam);
40 DWORD dwProcessId = 0;
41 DWORD_PTR dwResult = 0;
42 BOOL fQueryEndSession = WM_QUERYENDSESSION == pPM->dwMessageId;
43 BOOL fContinueWindowsInProcess = TRUE; // assume we will send message to all top-level windows in a process.
44
45 ::GetWindowThreadProcessId(hwnd, &dwProcessId);
46
47 // check if the process Id is the one we're looking for
48 if (dwProcessId != pPM->dwProcessId)
49 {
50 return TRUE;
51 }
52
53 WcaLog(LOGMSG_VERBOSE, "Sending message to process id 0x%x", dwProcessId);
54
55 if (::SendMessageTimeoutW(hwnd, pPM->dwMessageId, 0, fQueryEndSession ? ENDSESSION_CLOSEAPP : 0, SMTO_BLOCK, pPM->dwTimeout, &dwResult))
56 {
57 WcaLog(LOGMSG_VERBOSE, "Result 0x%x", dwResult);
58
59 if (fQueryEndSession)
60 {
61 // If application said it was okay to close, do that.
62 if (dwResult)
63 {
64 ::SendMessageTimeoutW(hwnd, WM_ENDSESSION, TRUE, ENDSESSION_CLOSEAPP, SMTO_BLOCK, pPM->dwTimeout, &dwResult);
65 }
66 else // application said don't try to close it, so don't bother sending messages to any other top-level windows.
67 {
68 fContinueWindowsInProcess = FALSE;
69 }
70 }
71 }
72 else // log result message.
73 {
74 WcaLog(LOGMSG_VERBOSE, "Failed to send message id: %u, error: 0x%x", pPM->dwMessageId, ::GetLastError());
75 }
76
77 // so we know we succeeded
78 ::SetLastError(ERROR_SUCCESS);
79
80 return fContinueWindowsInProcess;
81}
82
83/******************************************************************
84 PromptToContinue - displays the prompt if the application is still
85 running.
86
87******************************************************************/
88static HRESULT PromptToContinue(
89 __in_z LPCWSTR wzApplication,
90 __in_z LPCWSTR wzPrompt
91 )
92{
93 HRESULT hr = S_OK;
94 UINT er = ERROR_SUCCESS;
95 PMSIHANDLE hRecMessage = NULL;
96 DWORD *prgProcessIds = NULL;
97 DWORD cProcessIds = 0;
98
99 hRecMessage = ::MsiCreateRecord(1);
100 ExitOnNull(hRecMessage, hr, E_OUTOFMEMORY, "Failed to create record for prompt.");
101
102 er = ::MsiRecordSetStringW(hRecMessage, 0, wzPrompt);
103 ExitOnWin32Error(er, hr, "Failed to set prompt record field string");
104
105 do
106 {
107 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
108 if (SUCCEEDED(hr) && 0 < cProcessIds)
109 {
110 er = WcaProcessMessage(static_cast<INSTALLMESSAGE>(INSTALLMESSAGE_WARNING | MB_ABORTRETRYIGNORE | MB_DEFBUTTON3 | MB_ICONWARNING), hRecMessage);
111 if (IDABORT == er)
112 {
113 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
114 }
115 else if (IDRETRY == er)
116 {
117 hr = S_FALSE;
118 }
119 else if (IDIGNORE == er)
120 {
121 hr = S_OK;
122 }
123 else
124 {
125 ExitOnWin32Error(er, hr, "Unexpected return value from prompt to continue.");
126 }
127 }
128
129 ReleaseNullMem(prgProcessIds);
130 cProcessIds = 0;
131 } while (S_FALSE == hr);
132
133LExit:
134 ReleaseMem(prgProcessIds);
135 return hr;
136}
137
138/******************************************************************
139 SendProcessMessage - helper function to enumerate the top-level
140 windows and send to all matching a process ID.
141
142******************************************************************/
143void SendProcessMessage(
144 __in DWORD dwProcessId,
145 __in DWORD dwMessageId,
146 __in DWORD dwTimeout
147 )
148{
149 WcaLog(LOGMSG_VERBOSE, "Attempting to send process id 0x%x message id: %u", dwProcessId, dwMessageId);
150
151 PROCESS_AND_MESSAGE pm = { };
152 pm.dwProcessId = dwProcessId;
153 pm.dwMessageId = dwMessageId;
154 pm.dwTimeout = dwTimeout;
155
156 if (!::EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&pm)))
157 {
158 DWORD dwLastError = ::GetLastError();
159 if (ERROR_SUCCESS != dwLastError)
160 {
161 WcaLog(LOGMSG_VERBOSE, "CloseApp enumeration error: 0x%x", dwLastError);
162 }
163 }
164}
165
166/******************************************************************
167 SendApplicationMessage - helper function to iterate through the
168 processes for the specified application and send all
169 applicable process Ids a message and give them time to process
170 the message.
171
172******************************************************************/
173void SendApplicationMessage(
174 __in LPCWSTR wzApplication,
175 __in DWORD dwMessageId,
176 __in DWORD dwTimeout
177 )
178{
179 DWORD *prgProcessIds = NULL;
180 DWORD cProcessIds = 0, iProcessId;
181 HRESULT hr = S_OK;
182
183 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
184
185 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
186
187 if (SUCCEEDED(hr) && 0 < cProcessIds)
188 {
189 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, attempting to send message.", wzApplication, cProcessIds);
190
191 for (iProcessId = 0; iProcessId < cProcessIds; ++iProcessId)
192 {
193 SendProcessMessage(prgProcessIds[iProcessId], dwMessageId, dwTimeout);
194 }
195
196 ProcWaitForIds(prgProcessIds, cProcessIds, dwTimeout);
197 }
198
199 ReleaseMem(prgProcessIds);
200}
201
202/******************************************************************
203 SetRunningProcessProperty - helper function that sets the specified
204 property if there are any instances of the specified executable
205 running. Useful to show custom UI to ask for shutdown.
206******************************************************************/
207void SetRunningProcessProperty(
208 __in LPCWSTR wzApplication,
209 __in LPCWSTR wzProperty
210 )
211{
212 DWORD *prgProcessIds = NULL;
213 DWORD cProcessIds = 0;
214 HRESULT hr = S_OK;
215
216 WcaLog(LOGMSG_VERBOSE, "Checking App: %ls ", wzApplication);
217
218 hr = ProcFindAllIdsFromExeName(wzApplication, &prgProcessIds, &cProcessIds);
219
220 if (SUCCEEDED(hr) && 0 < cProcessIds)
221 {
222 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, %d processes, setting '%ls' property.", wzApplication, cProcessIds, wzProperty);
223 WcaSetIntProperty(wzProperty, cProcessIds);
224 }
225
226 ReleaseMem(prgProcessIds);
227}
228
229/******************************************************************
230 TerminateProcesses - helper function that kills the provided set of
231 process ids such that they return a particular exit code.
232******************************************************************/
233void TerminateProcesses(
234 __in_ecount(cProcessIds) DWORD rgdwProcessIds[],
235 __in DWORD cProcessIds,
236 __in DWORD dwExitCode
237 )
238{
239 for (DWORD i = 0; i < cProcessIds; ++i)
240 {
241 HANDLE hProcess = ::OpenProcess(PROCESS_TERMINATE, FALSE, rgdwProcessIds[i]);
242 if (hProcess)
243 {
244 ::TerminateProcess(hProcess, dwExitCode);
245 ::CloseHandle(hProcess);
246 }
247 }
248}
249
250/******************************************************************
251 WixCloseApplications - entry point for WixCloseApplications Custom Action
252
253 called as Type 1 CustomAction (binary DLL) from Windows Installer
254 in InstallExecuteSequence before InstallFiles
255******************************************************************/
256extern "C" UINT __stdcall WixCloseApplications(
257 __in MSIHANDLE hInstall
258 )
259{
260 //AssertSz(FALSE, "debug WixCloseApplications");
261 HRESULT hr = S_OK;
262 UINT er = ERROR_SUCCESS;
263
264 LPWSTR pwzData = NULL;
265 LPWSTR pwzId = NULL;
266 LPWSTR pwzTarget = NULL;
267 LPWSTR pwzDescription = NULL;
268 LPWSTR pwzCondition = NULL;
269 LPWSTR pwzProperty = NULL;
270 DWORD dwAttributes = 0;
271 DWORD dwTimeout = 0;
272 DWORD dwTerminateExitCode = 0;
273 MSICONDITION condition = MSICONDITION_NONE;
274
275 DWORD cCloseApps = 0;
276
277 PMSIHANDLE hView = NULL;
278 PMSIHANDLE hRec = NULL;
279 MSIHANDLE hListboxTable = NULL;
280 MSIHANDLE hListboxColumns = NULL;
281
282 LPWSTR pwzCustomActionData = NULL;
283 //DWORD cchCustomActionData = 0;
284
285 //
286 // initialize
287 //
288 hr = WcaInitialize(hInstall, "WixCloseApplications");
289 ExitOnFailure(hr, "failed to initialize");
290
291 //
292 // loop through all the objects to be secured
293 //
294 hr = WcaOpenExecuteView(wzQUERY_CLOSEAPPS, &hView);
295 ExitOnFailure(hr, "failed to open view on Wix4CloseApplication table");
296 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
297 {
298 hr = WcaGetRecordString(hRec, QCA_ID, &pwzId);
299 ExitOnFailure(hr, "failed to get id from Wix4CloseApplication table");
300
301 hr = WcaGetRecordString(hRec, QCA_CONDITION, &pwzCondition);
302 ExitOnFailure(hr, "failed to get condition from Wix4CloseApplication table");
303
304 if (pwzCondition && *pwzCondition)
305 {
306 condition = ::MsiEvaluateConditionW(hInstall, pwzCondition);
307 if (MSICONDITION_ERROR == condition)
308 {
309 hr = E_INVALIDARG;
310 ExitOnFailure(hr, "failed to process condition for Wix4CloseApplication '%ls'", pwzId);
311 }
312 else if (MSICONDITION_FALSE == condition)
313 {
314 continue; // skip processing this target
315 }
316 }
317
318 hr = WcaGetRecordFormattedString(hRec, QCA_TARGET, &pwzTarget);
319 ExitOnFailure(hr, "failed to get target from Wix4CloseApplication table");
320
321 hr = WcaGetRecordFormattedString(hRec, QCA_DESCRIPTION, &pwzDescription);
322 ExitOnFailure(hr, "failed to get description from Wix4CloseApplication table");
323
324 hr = WcaGetRecordInteger(hRec, QCA_ATTRIBUTES, reinterpret_cast<int*>(&dwAttributes));
325 ExitOnFailure(hr, "failed to get attributes from Wix4CloseApplication table");
326
327 hr = WcaGetRecordFormattedString(hRec, QCA_PROPERTY, &pwzProperty);
328 ExitOnFailure(hr, "failed to get property from Wix4CloseApplication table");
329
330 hr = WcaGetRecordInteger(hRec, QCA_TERMINATEEXITCODE, reinterpret_cast<int*>(&dwTerminateExitCode));
331 if (S_FALSE == hr)
332 {
333 dwTerminateExitCode = 0;
334 hr = S_OK;
335 }
336 ExitOnFailure(hr, "failed to get terminate exit-code from Wix4CloseApplication table");
337
338 hr = WcaGetRecordInteger(hRec, QCA_TIMEOUT, reinterpret_cast<int*>(&dwTimeout));
339 if (S_FALSE == hr)
340 {
341 dwTimeout = DEFAULT_PROCESS_EXIT_WAIT_TIME;
342 hr = S_OK;
343 }
344 ExitOnFailure(hr, "failed to get timeout from Wix4CloseApplication table");
345
346 // Before trying any changes to the machine, prompt if requested.
347 if (dwAttributes & CLOSEAPP_ATTRIBUTE_PROMPTTOCONTINUE)
348 {
349 hr = PromptToContinue(pwzTarget, pwzDescription ? pwzDescription : L"");
350 if (HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr)
351 {
352 // Skip error message if user canceled.
353 ExitFunction();
354 }
355 ExitOnFailure(hr, "Failure while prompting user to continue to close application.");
356 }
357
358 //
359 // send WM_CLOSE or WM_QUERYENDSESSION to currently running applications
360 //
361 if (dwAttributes & CLOSEAPP_ATTRIBUTE_CLOSEMESSAGE)
362 {
363 SendApplicationMessage(pwzTarget, WM_CLOSE, dwTimeout);
364 }
365
366 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ENDSESSIONMESSAGE)
367 {
368 SendApplicationMessage(pwzTarget, WM_QUERYENDSESSION, dwTimeout);
369 }
370
371 //
372 // Pass the targets to the deferred action in case the app comes back
373 // even if we close it now.
374 //
375 if (dwAttributes & (CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE | CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE | CLOSEAPP_ATTRIBUTE_REBOOTPROMPT | CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS))
376 {
377 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
378 ExitOnFailure(hr, "failed to add target data to CustomActionData");
379
380 hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData);
381 ExitOnFailure(hr, "failed to add attribute data to CustomActionData");
382
383 hr = WcaWriteIntegerToCaData(dwTimeout, &pwzCustomActionData);
384 ExitOnFailure(hr, "failed to add timeout data to CustomActionData");
385
386 hr = WcaWriteIntegerToCaData(dwTerminateExitCode, &pwzCustomActionData);
387 ExitOnFailure(hr, "failed to add timeout data to CustomActionData");
388 }
389
390 if (pwzProperty && *pwzProperty)
391 {
392 SetRunningProcessProperty(pwzTarget, pwzProperty);
393 }
394
395 ++cCloseApps;
396 }
397
398 // if we looped through all records all is well
399 if (E_NOMOREITEMS == hr)
400 {
401 hr = S_OK;
402 }
403 ExitOnFailure(hr, "failed while looping through all apps to close");
404
405 //
406 // Do the UI dance now.
407 //
408 /*
409
410 TODO: Do this eventually
411
412 if (cCloseApps)
413 {
414 while (TRUE)
415 {
416 for (DWORD i = 0; i < cCloseApps; ++i)
417 {
418 hr = WcaAddTempRecord(&hListboxTable, &hListboxColumns, L"ListBox", NULL, 0, 4, L"FileInUseProcess", i, target, description);
419 if (FAILED(hr))
420 {
421 }
422 }
423 }
424 }
425 */
426
427 //
428 // schedule the custom action and add to progress bar
429 //
430 if (pwzCustomActionData && *pwzCustomActionData)
431 {
432 Assert(0 < cCloseApps);
433
434 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CloseApplicationsDeferred"), pwzCustomActionData, cCloseApps * COST_CLOSEAPP);
435 ExitOnFailure(hr, "failed to schedule CloseApplicationsDeferred action");
436 }
437
438LExit:
439 if (hListboxColumns)
440 {
441 ::MsiCloseHandle(hListboxColumns);
442 }
443 if (hListboxTable)
444 {
445 ::MsiCloseHandle(hListboxTable);
446 }
447
448 ReleaseStr(pwzCustomActionData);
449 ReleaseStr(pwzData);
450 ReleaseStr(pwzProperty);
451 ReleaseStr(pwzCondition);
452 ReleaseStr(pwzDescription);
453 ReleaseStr(pwzTarget);
454 ReleaseStr(pwzId);
455
456 if (FAILED(hr))
457 {
458 er = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT) == hr ? ERROR_INSTALL_USEREXIT : ERROR_INSTALL_FAILURE;
459 }
460 return WcaFinalize(er);
461}
462
463
464/******************************************************************
465 WixCloseApplicationsDeferred - entry point for
466 WixCloseApplicationsDeferred Custom Action
467 called as Type 1025 CustomAction
468 (deferred binary DLL)
469
470 NOTE: deferred CustomAction since it modifies the machine
471 NOTE: CustomActionData == wzTarget\tdwAttributes\tdwTimeout\tdwTerminateExitCode\t...
472******************************************************************/
473extern "C" UINT __stdcall WixCloseApplicationsDeferred(
474 __in MSIHANDLE hInstall
475 )
476{
477 //AssertSz(FALSE, "debug WixCloseApplicationsDeferred");
478 HRESULT hr = S_OK;
479 DWORD er = ERROR_SUCCESS;
480
481 LPWSTR pwz = NULL;
482 LPWSTR pwzData = NULL;
483 LPWSTR pwzTarget = NULL;
484 DWORD dwAttributes = 0;
485 DWORD dwTimeout = 0;
486 DWORD dwTerminateExitCode = 0;
487
488 DWORD *prgProcessIds = NULL;
489 DWORD cProcessIds = 0;
490
491 //
492 // initialize
493 //
494 hr = WcaInitialize(hInstall, "WixCloseApplicationsDeferred");
495 ExitOnFailure(hr, "failed to initialize");
496
497 hr = WcaGetProperty(L"CustomActionData", &pwzData);
498 ExitOnFailure(hr, "failed to get CustomActionData");
499
500 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
501
502 pwz = pwzData;
503
504 //
505 // loop through all the passed in data
506 //
507 while (pwz && *pwz)
508 {
509 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
510 ExitOnFailure(hr, "failed to process target from CustomActionData");
511
512 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes));
513 ExitOnFailure(hr, "failed to process attributes from CustomActionData");
514
515 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwTimeout));
516 ExitOnFailure(hr, "failed to process timeout from CustomActionData");
517
518 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwTerminateExitCode));
519 ExitOnFailure(hr, "failed to process terminate exit code from CustomActionData");
520
521 WcaLog(LOGMSG_VERBOSE, "Checking for App: %ls Attributes: %d", pwzTarget, dwAttributes);
522
523 //
524 // send WM_CLOSE or WM_QUERYENDSESSION to currently running applications
525 //
526 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDCLOSEMESSAGE)
527 {
528 SendApplicationMessage(pwzTarget, WM_CLOSE, dwTimeout);
529 }
530
531 if (dwAttributes & CLOSEAPP_ATTRIBUTE_ELEVATEDENDSESSIONMESSAGE)
532 {
533 SendApplicationMessage(pwzTarget, WM_QUERYENDSESSION, dwTimeout);
534 }
535
536 // If we find that an app that we need closed is still runing, require a
537 // restart or kill the process as directed.
538 ProcFindAllIdsFromExeName(pwzTarget, &prgProcessIds, &cProcessIds);
539 if (0 < cProcessIds)
540 {
541 if (dwAttributes & CLOSEAPP_ATTRIBUTE_REBOOTPROMPT)
542 {
543 WcaLog(LOGMSG_VERBOSE, "App: %ls found running, requiring a reboot.", pwzTarget);
544
545 WcaDeferredActionRequiresReboot();
546 }
547 else if (dwAttributes & CLOSEAPP_ATTRIBUTE_TERMINATEPROCESS)
548 {
549 TerminateProcesses(prgProcessIds, cProcessIds, dwTerminateExitCode);
550 }
551 }
552
553 hr = WcaProgressMessage(COST_CLOSEAPP, FALSE);
554 ExitOnFailure(hr, "failed to send progress message");
555 }
556
557LExit:
558 ReleaseMem(prgProcessIds);
559
560 ReleaseStr(pwzTarget);
561 ReleaseStr(pwzData);
562
563 if (FAILED(hr))
564 {
565 er = ERROR_INSTALL_FAILURE;
566 }
567 return WcaFinalize(er);
568}
diff --git a/src/ext/Util/ca/CustomMsiErrors.h b/src/ext/Util/ca/CustomMsiErrors.h
new file mode 100644
index 00000000..3218b61b
--- /dev/null
+++ b/src/ext/Util/ca/CustomMsiErrors.h
@@ -0,0 +1,32 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#define msierrSecureObjectsFailedCreateSD 25520
6#define msierrSecureObjectsFailedSet 25521
7#define msierrSecureObjectsUnknownType 25522
8
9#define msierrXmlFileFailedRead 25530
10#define msierrXmlFileFailedOpen 25531
11#define msierrXmlFileFailedSelect 25532
12#define msierrXmlFileFailedSave 25533
13
14#define msierrXmlConfigFailedRead 25540
15#define msierrXmlConfigFailedOpen 25541
16#define msierrXmlConfigFailedSelect 25542
17#define msierrXmlConfigFailedSave 25543
18
19#define msierrPERFMONFailedRegisterDLL 26251
20#define msierrPERFMONFailedUnregisterDLL 26252
21#define msierrInstallPerfCounterData 26253
22#define msierrUninstallPerfCounterData 26254
23
24#define msierrSMBFailedCreate 26301
25#define msierrSMBFailedDrop 26302
26#define msierrUSRFailedUserCreate 26401
27#define msierrUSRFailedUserCreatePswd 26402
28#define msierrUSRFailedUserGroupAdd 26403
29#define msierrUSRFailedUserCreateExists 26404
30#define msierrUSRFailedGrantLogonAsService 26405
31
32//Last available is 26450 \ No newline at end of file
diff --git a/src/ext/Util/ca/FormatFiles.cpp b/src/ext/Util/ca/FormatFiles.cpp
new file mode 100644
index 00000000..d1533999
--- /dev/null
+++ b/src/ext/Util/ca/FormatFiles.cpp
@@ -0,0 +1,221 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5const UINT COST_FILEFORMATTING = 2000;
6
7
8//
9// WixSchedFormatFiles - immediate CA to schedule format files CAs
10//
11extern "C" UINT __stdcall WixSchedFormatFiles(
12 __in MSIHANDLE hInstall
13 )
14{
15 HRESULT hr = S_OK;
16 UINT er = ERROR_SUCCESS;
17 PSCZ sczBinaryKey;
18 PSCZ sczFileKey;
19 PSCZ sczComponentKey;
20 PSCZ sczFormattedFile;
21 PSCZ sczFilePath;
22 PMSIHANDLE hView;
23 PMSIHANDLE hRec;
24 PSCZ sczFileContent;
25 PSCZ sczFormattedContent;
26 PSCZ sczExecCustomActionData;
27 PSCZ sczRollbackCustomActionData;
28
29 LPCWSTR wzQuery =
30 L"SELECT `Wix4FormatFile`.`Binary_`, `Wix4FormatFile`.`File_`, `File`.`Component_` "
31 L"FROM `Wix4FormatFile`, `File` "
32 L"WHERE `Wix4FormatFile`.`File_` = `File`.`File`";
33 enum eQuery { eqBinaryKey = 1, eqFileKey, eqComponentKey };
34
35 // initialize
36 hr = WcaInitialize(hInstall, "WixSchedFormatFiles");
37 ExitOnFailure(hr, "Failed to initialize for WixSchedFormatFiles.");
38
39 // query and loop through all the files
40 hr = WcaOpenExecuteView(wzQuery, &hView);
41 ExitOnFailure(hr, "Failed to open view on Wix4FormatFile table");
42
43 DWORD cFiles = 0;
44 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
45 {
46 ++cFiles;
47
48 hr = WcaGetRecordString(hRec, eqBinaryKey, &sczBinaryKey);
49 ExitOnFailure(hr, "Failed to get Binary table key.");
50
51 hr = WcaGetRecordString(hRec, eqFileKey, &sczFileKey);
52 ExitOnFailure(hr, "Failed to get File table key.");
53
54 hr = WcaGetRecordString(hRec, eqComponentKey, &sczComponentKey);
55 ExitOnFailure(hr, "Failed to get Component table key.");
56
57 // we need to know if the component's being installed, uninstalled, or reinstalled
58 WCA_TODO todo = WcaGetComponentToDo(sczComponentKey);
59 if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
60 {
61 // turn the file key into the path to the target file
62 hr = StrAllocFormatted(&sczFormattedFile, L"[#%ls]", sczFileKey);
63 ExitOnFailure(hr, "Failed to format file string for file: %ls", sczFileKey);
64 hr = WcaGetFormattedString(sczFormattedFile, &sczFilePath);
65 ExitOnFailure(hr, "Failed to get path for file: %ls", sczFileKey);
66
67 // extract binary to string
68 WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
69 hr = WcaExtractBinaryToString(sczBinaryKey, &sczFileContent, &encoding);
70 ExitOnFailure(hr, "Failed to extract binary: %ls", sczBinaryKey);
71
72 // format string
73 hr = WcaGetFormattedString(sczFileContent, &sczFormattedContent);
74 ExitOnFailure(hr, "Failed to format file content: %ls", sczFileContent);
75
76 // write to deferred custom action data
77 hr = WcaWriteStringToCaData(sczFilePath, &sczExecCustomActionData);
78 ExitOnFailure(hr, "Failed to write deferred custom action data for file: %ls", sczFilePath);
79
80 hr = WcaWriteIntegerToCaData(encoding, &sczExecCustomActionData);
81 ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);
82
83 hr = WcaWriteStringToCaData(sczFormattedContent, &sczExecCustomActionData);
84 ExitOnFailure(hr, "Failed to write deferred custom action data for file content: %ls", sczFilePath);
85
86 // write to rollback custom action data
87 hr = WcaWriteStringToCaData(sczFilePath, &sczRollbackCustomActionData);
88 ExitOnFailure(hr, "Failed to write rollback custom action data for file: %ls", sczFilePath);
89
90 hr = WcaWriteIntegerToCaData(encoding, &sczRollbackCustomActionData);
91 ExitOnFailure(hr, "Failed to write deferred custom action data for encoding: %d", encoding);
92
93 hr = WcaWriteStringToCaData(sczFileContent, &sczRollbackCustomActionData);
94 ExitOnFailure(hr, "Failed to write rollback custom action data for file content: %ls", sczFilePath);
95 }
96 }
97
98 // reaching the end of the list is actually a good thing, not an error
99 if (E_NOMOREITEMS == hr)
100 {
101 hr = S_OK;
102 }
103 ExitOnFailure(hr, "Failure occurred while processing Wix4FormatFile table");
104
105 // schedule deferred CAs if there's anything to do
106 if (sczRollbackCustomActionData && *sczRollbackCustomActionData)
107 {
108 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackFormatFiles"), sczRollbackCustomActionData, cFiles * COST_FILEFORMATTING);
109 ExitOnFailure(hr, "Failed to schedule RollbackFormatFiles");
110 }
111
112 if (sczExecCustomActionData && *sczExecCustomActionData)
113 {
114 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecFormatFiles"), sczExecCustomActionData, cFiles * COST_FILEFORMATTING);
115 ExitOnFailure(hr, "Failed to schedule ExecFormatFiles");
116 }
117
118LExit:
119 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
120}
121
122
123//
124// WixExecFormatFiles - deferred and rollback CAs to write formatted files
125//
126extern "C" UINT __stdcall WixExecFormatFiles(
127 __in MSIHANDLE hInstall
128 )
129{
130 HRESULT hr = S_OK;
131 UINT er = ERROR_SUCCESS;
132 PSCZ sczCustomActionData;
133 LPWSTR pwz = NULL;
134 PSCZ sczFilePath;
135 PSCZ sczFileContent;
136 LPSTR psz = NULL;
137
138 // initialize
139 hr = WcaInitialize(hInstall, "WixExecFormatFiles");
140 ExitOnFailure(hr, "Failed to initialize for WixExecFormatFiles.");
141
142 hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData);
143 ExitOnFailure(hr, "Failed to get CustomActionData.");
144#ifdef _DEBUG
145 WcaLog(LOGMSG_STANDARD, "CustomActionData: %ls", sczCustomActionData);
146#endif
147
148 // loop through all the passed in data
149 pwz = sczCustomActionData;
150 while (pwz && *pwz)
151 {
152 // extract the custom action data
153 hr = WcaReadStringFromCaData(&pwz, &sczFilePath);
154 ExitOnFailure(hr, "Failed to read file path from custom action data");
155
156 WCA_ENCODING encoding = WCA_ENCODING_UNKNOWN;
157 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&encoding));
158 ExitOnFailure(hr, "Failed to read encoding from custom action data");
159
160 hr = WcaReadStringFromCaData(&pwz, &sczFileContent);
161 ExitOnFailure(hr, "Failed to read file content from custom action data");
162
163 // re-encode content
164 LPCBYTE pbData = NULL;
165 size_t cbData = 0;
166 switch (encoding)
167 {
168 case WCA_ENCODING_UTF_16:
169 pbData = reinterpret_cast<LPCBYTE>(LPCWSTR(sczFileContent));
170 cbData = lstrlenW(sczFileContent) * sizeof(WCHAR);
171 break;
172
173 case WCA_ENCODING_UTF_8:
174 hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_UTF8);
175 ExitOnFailure(hr, "Failed to convert Unicode to UTF-8.");
176 pbData = reinterpret_cast<LPCBYTE>(psz);
177
178 hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
179 ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
180 break;
181
182 case WCA_ENCODING_ANSI:
183 hr = StrAnsiAllocString(&psz, sczFileContent, 0, CP_ACP);
184 ExitOnFailure(hr, "Failed to convert Unicode to ANSI.");
185 pbData = reinterpret_cast<LPCBYTE>(psz);
186
187 hr = ::StringCbLengthA(psz, STRSAFE_MAX_CCH, &cbData);
188 ExitOnFailure(hr, "Failed to count UTF-8 bytes.");
189 break;
190
191 default:
192 break;
193 }
194
195#ifdef _DEBUG
196 WcaLog(LOGMSG_STANDARD, "File: %ls", sczCustomActionData);
197 WcaLog(LOGMSG_STANDARD, "Content: %ls", sczFileContent);
198#endif
199
200 // write file and preserve modified time
201 FILETIME filetime;
202
203 hr = FileGetTime(sczFilePath, NULL, NULL, &filetime);
204 ExitOnFailure(hr, "Failed to get modified time of file : %ls", sczFilePath);
205
206 hr = FileWrite(sczFilePath, FILE_ATTRIBUTE_NORMAL, pbData, cbData, NULL);
207 ExitOnFailure(hr, "Failed to write file content: %ls", sczFilePath);
208
209 hr = FileSetTime(sczFilePath, NULL, NULL, &filetime);
210 ExitOnFailure(hr, "Failed to set modified time of file : %ls", sczFilePath);
211
212 // Tick the progress bar
213 hr = WcaProgressMessage(COST_FILEFORMATTING, FALSE);
214 ExitOnFailure(hr, "Failed to tick progress bar for file: %ls", sczFilePath);
215 }
216
217LExit:
218 ReleaseStr(psz);
219
220 return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
221}
diff --git a/src/ext/Util/ca/OsInfo.cpp b/src/ext/Util/ca/OsInfo.cpp
new file mode 100644
index 00000000..4783673e
--- /dev/null
+++ b/src/ext/Util/ca/OsInfo.cpp
@@ -0,0 +1,487 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// constants we'll pick up from later SDKs
6#define SM_TABLETPC 86
7#define SM_MEDIACENTER 87
8#define SM_STARTER 88
9#define SM_SERVERR2 89
10#define VER_SUITE_WH_SERVER 0x00008000
11
12/********************************************************************
13WixQueryOsInfo - entry point for WixQueryOsInfo custom action
14
15 Called as Type 1 custom action (DLL from the Binary table) from
16 Windows Installer to set properties that identify OS information
17 and predefined directories
18********************************************************************/
19extern "C" UINT __stdcall WixQueryOsInfo(
20 __in MSIHANDLE hInstall
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD er = ERROR_SUCCESS;
25 OSVERSIONINFOEXW ovix = {0};
26
27 hr = WcaInitialize(hInstall, "WixQueryOsInfo");
28 ExitOnFailure(hr, "WixQueryOsInfo failed to initialize");
29
30 // identify product suites
31 ovix.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
32 #pragma warning(suppress: 4996) //TODO: use osutil
33 ::GetVersionExW(reinterpret_cast<LPOSVERSIONINFOW>(&ovix));
34
35 if (VER_SUITE_SMALLBUSINESS == (ovix.wSuiteMask & VER_SUITE_SMALLBUSINESS))
36 {
37 WcaSetIntProperty(L"WIX_SUITE_SMALLBUSINESS", 1);
38 }
39
40 if (VER_SUITE_ENTERPRISE == (ovix.wSuiteMask & VER_SUITE_ENTERPRISE))
41 {
42 WcaSetIntProperty(L"WIX_SUITE_ENTERPRISE", 1);
43 }
44
45 if (VER_SUITE_BACKOFFICE == (ovix.wSuiteMask & VER_SUITE_BACKOFFICE))
46 {
47 WcaSetIntProperty(L"WIX_SUITE_BACKOFFICE", 1);
48 }
49
50 if (VER_SUITE_COMMUNICATIONS == (ovix.wSuiteMask & VER_SUITE_COMMUNICATIONS))
51 {
52 WcaSetIntProperty(L"WIX_SUITE_COMMUNICATIONS", 1);
53 }
54
55 if (VER_SUITE_TERMINAL == (ovix.wSuiteMask & VER_SUITE_TERMINAL))
56 {
57 WcaSetIntProperty(L"WIX_SUITE_TERMINAL", 1);
58 }
59
60 if (VER_SUITE_SMALLBUSINESS_RESTRICTED == (ovix.wSuiteMask & VER_SUITE_SMALLBUSINESS_RESTRICTED))
61 {
62 WcaSetIntProperty(L"WIX_SUITE_SMALLBUSINESS_RESTRICTED", 1);
63 }
64
65 if (VER_SUITE_EMBEDDEDNT == (ovix.wSuiteMask & VER_SUITE_EMBEDDEDNT))
66 {
67 WcaSetIntProperty(L"WIX_SUITE_EMBEDDEDNT", 1);
68 }
69
70 if (VER_SUITE_DATACENTER == (ovix.wSuiteMask & VER_SUITE_DATACENTER))
71 {
72 WcaSetIntProperty(L"WIX_SUITE_DATACENTER", 1);
73 }
74
75 if (VER_SUITE_SINGLEUSERTS == (ovix.wSuiteMask & VER_SUITE_SINGLEUSERTS))
76 {
77 WcaSetIntProperty(L"WIX_SUITE_SINGLEUSERTS", 1);
78 }
79
80 if (VER_SUITE_PERSONAL == (ovix.wSuiteMask & VER_SUITE_PERSONAL))
81 {
82 WcaSetIntProperty(L"WIX_SUITE_PERSONAL", 1);
83 }
84
85 if (VER_SUITE_BLADE == (ovix.wSuiteMask & VER_SUITE_BLADE))
86 {
87 WcaSetIntProperty(L"WIX_SUITE_BLADE", 1);
88 }
89
90 if (VER_SUITE_EMBEDDED_RESTRICTED == (ovix.wSuiteMask & VER_SUITE_EMBEDDED_RESTRICTED))
91 {
92 WcaSetIntProperty(L"WIX_SUITE_EMBEDDED_RESTRICTED", 1);
93 }
94
95 if (VER_SUITE_SECURITY_APPLIANCE == (ovix.wSuiteMask & VER_SUITE_SECURITY_APPLIANCE))
96 {
97 WcaSetIntProperty(L"WIX_SUITE_SECURITY_APPLIANCE", 1);
98 }
99
100 if (VER_SUITE_STORAGE_SERVER == (ovix.wSuiteMask & VER_SUITE_STORAGE_SERVER))
101 {
102 WcaSetIntProperty(L"WIX_SUITE_STORAGE_SERVER", 1);
103 }
104
105 if (VER_SUITE_COMPUTE_SERVER == (ovix.wSuiteMask & VER_SUITE_COMPUTE_SERVER))
106 {
107 WcaSetIntProperty(L"WIX_SUITE_COMPUTE_SERVER", 1);
108 }
109
110 if (VER_SUITE_WH_SERVER == (ovix.wSuiteMask & VER_SUITE_WH_SERVER))
111 {
112 WcaSetIntProperty(L"WIX_SUITE_WH_SERVER", 1);
113 }
114
115 // only for XP and later
116 if (5 < ovix.dwMajorVersion || (5 == ovix.dwMajorVersion && 0 < ovix.dwMinorVersion))
117 {
118 if (::GetSystemMetrics(SM_SERVERR2))
119 {
120 WcaSetIntProperty(L"WIX_SUITE_SERVERR2", 1);
121 }
122
123 if (::GetSystemMetrics(SM_MEDIACENTER))
124 {
125 WcaSetIntProperty(L"WIX_SUITE_MEDIACENTER", 1);
126 }
127
128 if (::GetSystemMetrics(SM_STARTER))
129 {
130 WcaSetIntProperty(L"WIX_SUITE_STARTER", 1);
131 }
132
133 if (::GetSystemMetrics(SM_TABLETPC))
134 {
135 WcaSetIntProperty(L"WIX_SUITE_TABLETPC", 1);
136 }
137 }
138
139LExit:
140 if (FAILED(hr))
141 er = ERROR_INSTALL_FAILURE;
142 return WcaFinalize(er);
143}
144
145/********************************************************************
146WixQueryOsDirs - entry point for WixQueryOsDirs custom action
147
148 Called as Type 1 custom action (DLL from the Binary table) from
149 Windows Installer to set properties that identify predefined directories
150********************************************************************/
151extern "C" UINT __stdcall WixQueryOsDirs(
152 __in MSIHANDLE hInstall
153 )
154{
155 HRESULT hr = S_OK;
156 DWORD er = ERROR_SUCCESS;
157
158 hr = WcaInitialize(hInstall, "WixQueryOsDirs");
159 ExitOnFailure(hr, "WixQueryOsDirs failed to initialize");
160
161 // get the paths of the CSIDLs that represent real paths and for which MSI
162 // doesn't yet have standard folder properties
163 WCHAR path[MAX_PATH];
164 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_ADMINTOOLS, NULL, SHGFP_TYPE_CURRENT, path))
165 {
166 WcaSetProperty(L"WIX_DIR_ADMINTOOLS", path);
167 }
168
169 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_ALTSTARTUP, NULL, SHGFP_TYPE_CURRENT, path))
170 {
171 WcaSetProperty(L"WIX_DIR_ALTSTARTUP", path);
172 }
173
174 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_CDBURN_AREA, NULL, SHGFP_TYPE_CURRENT, path))
175 {
176 WcaSetProperty(L"WIX_DIR_CDBURN_AREA", path);
177 }
178
179 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_ADMINTOOLS, NULL, SHGFP_TYPE_CURRENT, path))
180 {
181 WcaSetProperty(L"WIX_DIR_COMMON_ADMINTOOLS", path);
182 }
183
184 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_ALTSTARTUP, NULL, SHGFP_TYPE_CURRENT, path))
185 {
186 WcaSetProperty(L"WIX_DIR_COMMON_ALTSTARTUP", path);
187 }
188
189 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_DOCUMENTS, NULL, SHGFP_TYPE_CURRENT, path))
190 {
191 WcaSetProperty(L"WIX_DIR_COMMON_DOCUMENTS", path);
192 }
193
194 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_FAVORITES, NULL, SHGFP_TYPE_CURRENT, path))
195 {
196 WcaSetProperty(L"WIX_DIR_COMMON_FAVORITES", path);
197 }
198
199 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_MUSIC, NULL, SHGFP_TYPE_CURRENT, path))
200 {
201 WcaSetProperty(L"WIX_DIR_COMMON_MUSIC", path);
202 }
203
204 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_PICTURES, NULL, SHGFP_TYPE_CURRENT, path))
205 {
206 WcaSetProperty(L"WIX_DIR_COMMON_PICTURES", path);
207 }
208
209 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COMMON_VIDEO, NULL, SHGFP_TYPE_CURRENT, path))
210 {
211 WcaSetProperty(L"WIX_DIR_COMMON_VIDEO", path);
212 }
213
214 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_COOKIES, NULL, SHGFP_TYPE_CURRENT, path))
215 {
216 WcaSetProperty(L"WIX_DIR_COOKIES", path);
217 }
218
219 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, SHGFP_TYPE_CURRENT, path))
220 {
221 WcaSetProperty(L"WIX_DIR_DESKTOP", path);
222 }
223
224 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_HISTORY, NULL, SHGFP_TYPE_CURRENT, path))
225 {
226 WcaSetProperty(L"WIX_DIR_HISTORY", path);
227 }
228
229 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_INTERNET_CACHE, NULL, SHGFP_TYPE_CURRENT, path))
230 {
231 WcaSetProperty(L"WIX_DIR_INTERNET_CACHE", path);
232 }
233
234 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, path))
235 {
236 WcaSetProperty(L"WIX_DIR_MYMUSIC", path);
237 }
238
239 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, path))
240 {
241 WcaSetProperty(L"WIX_DIR_MYPICTURES", path);
242 }
243
244 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_MYVIDEO, NULL, SHGFP_TYPE_CURRENT, path))
245 {
246 WcaSetProperty(L"WIX_DIR_MYVIDEO", path);
247 }
248
249 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_NETHOOD, NULL, SHGFP_TYPE_CURRENT, path))
250 {
251 WcaSetProperty(L"WIX_DIR_NETHOOD", path);
252 }
253
254 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, path))
255 {
256 WcaSetProperty(L"WIX_DIR_PERSONAL", path);
257 }
258
259 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PRINTHOOD, NULL, SHGFP_TYPE_CURRENT, path))
260 {
261 WcaSetProperty(L"WIX_DIR_PRINTHOOD", path);
262 }
263
264 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, path))
265 {
266 WcaSetProperty(L"WIX_DIR_PROFILE", path);
267 }
268
269 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_RECENT, NULL, SHGFP_TYPE_CURRENT, path))
270 {
271 WcaSetProperty(L"WIX_DIR_RECENT", path);
272 }
273
274 if (ERROR_SUCCESS == ::SHGetFolderPathW(NULL, CSIDL_RESOURCES, NULL, SHGFP_TYPE_CURRENT, path))
275 {
276 WcaSetProperty(L"WIX_DIR_RESOURCES", path);
277 }
278
279LExit:
280 if (FAILED(hr))
281 er = ERROR_INSTALL_FAILURE;
282 return WcaFinalize(er);
283}
284
285
286/********************************************************************
287SetPropertyWellKnownSID
288
289 Set a property with the localized name of a well known windows SID
290********************************************************************/
291static HRESULT SetPropertyWellKnownSID(
292 __in WELL_KNOWN_SID_TYPE sidType,
293 __in LPCWSTR wzPropertyName,
294 __in BOOL fIncludeDomainName
295 )
296{
297 HRESULT hr = S_OK;
298 PSID psid = NULL;
299 WCHAR wzRefDomain[MAX_PATH] = {0};
300 SID_NAME_USE nameUse;
301 DWORD refSize = MAX_PATH;
302 WCHAR wzName[MAX_PATH] = {0};
303 LPWSTR pwzPropertyValue = NULL;
304 DWORD size = MAX_PATH;
305
306 hr = AclGetWellKnownSid(sidType, &psid);
307 ExitOnFailure(hr, "Failed to get SID; skipping account %ls", wzPropertyName);
308
309 if (!::LookupAccountSidW(NULL, psid, wzName, &size, wzRefDomain, &refSize, &nameUse))
310 {
311 ExitWithLastError(hr, "Failed to look up account for SID; skipping account %ls.", wzPropertyName);
312 }
313
314 if (fIncludeDomainName)
315 {
316 hr = StrAllocFormatted(&pwzPropertyValue, L"%s\\%s", wzRefDomain, wzName);
317 ExitOnFailure(hr, "Failed to format property value");
318
319 hr = WcaSetProperty(wzPropertyName, pwzPropertyValue);
320 ExitOnFailure(hr, "Failed write domain\\name property");
321 }
322 else
323 {
324 hr = WcaSetProperty(wzPropertyName, wzName);
325 ExitOnFailure(hr, "Failed write to name-only property");
326 }
327
328LExit:
329 if (NULL != psid)
330 {
331 ::LocalFree(psid);
332 }
333 ReleaseStr(pwzPropertyValue);
334 return hr;
335}
336
337/********************************************************************
338WixQueryOsWellKnownSID - entry point for WixQueryOsWellKnownSID custom action
339
340 Called as Type 1 custom action (DLL from the Binary table) from
341 Windows Installer to set properties with the localized names of built-in
342 Windows Security IDs
343********************************************************************/
344extern "C" UINT __stdcall WixQueryOsWellKnownSID(
345 __in MSIHANDLE hInstall
346 )
347{
348 HRESULT hr = S_OK;
349 DWORD er = ERROR_SUCCESS;
350
351 hr = WcaInitialize(hInstall, "WixQueryOsWellKnownSID");
352 ExitOnFailure(hr, "WixQueryOsWellKnownSID failed to initialize");
353
354 SetPropertyWellKnownSID(WinLocalSystemSid, L"WIX_ACCOUNT_LOCALSYSTEM", TRUE);
355 SetPropertyWellKnownSID(WinLocalServiceSid, L"WIX_ACCOUNT_LOCALSERVICE", TRUE);
356 SetPropertyWellKnownSID(WinNetworkServiceSid, L"WIX_ACCOUNT_NETWORKSERVICE", TRUE);
357 SetPropertyWellKnownSID(WinBuiltinAdministratorsSid, L"WIX_ACCOUNT_ADMINISTRATORS", TRUE);
358 SetPropertyWellKnownSID(WinBuiltinUsersSid, L"WIX_ACCOUNT_USERS", TRUE);
359 SetPropertyWellKnownSID(WinBuiltinGuestsSid, L"WIX_ACCOUNT_GUESTS", TRUE);
360 SetPropertyWellKnownSID(WinBuiltinPerfLoggingUsersSid, L"WIX_ACCOUNT_PERFLOGUSERS", TRUE);
361 SetPropertyWellKnownSID(WinBuiltinPerfLoggingUsersSid, L"WIX_ACCOUNT_PERFLOGUSERS_NODOMAIN", FALSE);
362
363LExit:
364 if (FAILED(hr))
365 {
366 er = ERROR_INSTALL_FAILURE;
367 }
368 return WcaFinalize(er);
369}
370
371
372/********************************************************************
373DetectWDDMDriver
374
375 Set a property if the driver on the machine is a WDDM driver. One
376 reliable way to detect the presence of a WDDM driver is to try and
377 use the Direct3DCreate9Ex() function. This method attempts that
378 then sets the property appropriately.
379********************************************************************/
380static HRESULT DetectWDDMDriver()
381{
382 HRESULT hr = S_OK;
383 HMODULE hModule = NULL;
384
385 // Manually load the d3d9.dll library. If the library couldn't be loaded then we obviously won't be able
386 // to try calling the function so just return.
387 hr = LoadSystemLibrary(L"d3d9.dll", &hModule);
388 if (E_MODNOTFOUND == hr)
389 {
390 TraceError(hr, "Unable to load DirectX APIs, skipping WDDM driver check.");
391 ExitFunction1(hr = S_OK);
392 }
393 ExitOnFailure(hr, "Failed to the load the existing DirectX APIs.");
394
395 // Obtain the address of the Direct3DCreate9Ex function. If this fails we know it isn't a WDDM
396 // driver so just exit.
397 const void* Direct3DCreate9ExPtr = ::GetProcAddress(hModule, "Direct3DCreate9Ex");
398 ExitOnNull(Direct3DCreate9ExPtr, hr, S_OK, "Unable to load Direct3DCreateEx function, so the driver is not a WDDM driver.");
399
400 // At this point we know it's a WDDM driver so set the property.
401 hr = WcaSetIntProperty(L"WIX_WDDM_DRIVER_PRESENT", 1);
402 ExitOnFailure(hr, "Failed write property");
403
404LExit:
405 if (NULL != hModule)
406 {
407 FreeLibrary(hModule);
408 }
409
410 return hr;
411}
412
413/********************************************************************
414DetectIsCompositionEnabled
415
416 Set a property based on the return value of DwmIsCompositionEnabled().
417********************************************************************/
418static HRESULT DetectIsCompositionEnabled()
419{
420 HRESULT hr = S_OK;
421 HMODULE hModule = NULL;
422 BOOL compositionState = false;
423
424 // Manually load the d3d9.dll library. If the library can't load it's likely because we are not on a Vista
425 // OS. Just return ok, and the property won't get set.
426 hr = LoadSystemLibrary(L"dwmapi.dll", &hModule);
427 if (E_MODNOTFOUND == hr)
428 {
429 TraceError(hr, "Unable to load Vista desktop window manager APIs, skipping Composition Enabled check.");
430 ExitFunction1(hr = S_OK);
431 }
432 ExitOnFailure(hr, "Failed to load the existing window manager APIs.");
433
434 // If for some reason we can't get the function pointer that's ok, just return.
435 typedef HRESULT (WINAPI *DWMISCOMPOSITIONENABLEDPTR)(BOOL*);
436 DWMISCOMPOSITIONENABLEDPTR DwmIsCompositionEnabledPtr = (DWMISCOMPOSITIONENABLEDPTR)::GetProcAddress(hModule, "DwmIsCompositionEnabled");
437 ExitOnNull(hModule, hr, S_OK, "Unable to obtain function information, skipping Composition Enabled check.");
438
439 hr = DwmIsCompositionEnabledPtr(&compositionState);
440 ExitOnFailure(hr, "Failed to retrieve Composition state");
441
442 if (compositionState)
443 {
444 hr = WcaSetIntProperty(L"WIX_DWM_COMPOSITION_ENABLED", 1);
445 ExitOnFailure(hr, "Failed write property");
446 }
447
448LExit:
449 if (NULL != hModule)
450 {
451 FreeLibrary(hModule);
452 }
453 return hr;
454}
455
456/********************************************************************
457WixQueryOsDriverInfo - entry point for WixQueryOsDriverInfo custom action
458
459 Called as Type 1 custom action (DLL from the Binary table) from
460 Windows Installer to set properties about drivers installed on
461 the target machine
462********************************************************************/
463extern "C" UINT __stdcall WixQueryOsDriverInfo(
464 __in MSIHANDLE hInstall
465 )
466{
467 HRESULT hr = S_OK;
468 DWORD er = ERROR_SUCCESS;
469
470 hr = WcaInitialize(hInstall, "WixQueryOsDriverInfo");
471 ExitOnFailure(hr, "WixQueryOsDriverInfo failed to initialize");
472
473 // Detect the WDDM driver status
474 hr = DetectWDDMDriver();
475 ExitOnFailure(hr, "Failed to detect WIX_WDDM_DRIVER_PRESENT");
476
477 // Detect whether composition is enabled
478 hr = DetectIsCompositionEnabled();
479 ExitOnFailure(hr, "Failed to detect WIX_DWM_COMPOSITION_ENABLED");
480
481LExit:
482 if (FAILED(hr))
483 {
484 er = ERROR_INSTALL_FAILURE;
485 }
486 return WcaFinalize(er);
487}
diff --git a/src/ext/Util/ca/RemoveFoldersEx.cpp b/src/ext/Util/ca/RemoveFoldersEx.cpp
new file mode 100644
index 00000000..cbc7f4bb
--- /dev/null
+++ b/src/ext/Util/ca/RemoveFoldersEx.cpp
@@ -0,0 +1,243 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsRemoveFolderExQuery =
6 L"SELECT `Wix4RemoveFolderEx`, `Component_`, `Property`, `InstallMode`, `WixRemoveFolderEx`.`Condition`, `Component`.`Attributes`"
7 L"FROM `Wix4RemoveFolderEx``,`Component` "
8 L"WHERE `Wix4RemoveFolderEx`.`Component_`=`Component`.`Component`";
9enum eRemoveFolderExQuery { rfqId = 1, rfqComponent, rfqProperty, rfqMode, rfqCondition, rfqComponentAttributes };
10
11static HRESULT RecursePath(
12 __in_z LPCWSTR wzPath,
13 __in_z LPCWSTR wzId,
14 __in_z LPCWSTR wzComponent,
15 __in_z LPCWSTR wzProperty,
16 __in int iMode,
17 __in BOOL fDisableWow64Redirection,
18 __inout DWORD* pdwCounter,
19 __inout MSIHANDLE* phTable,
20 __inout MSIHANDLE* phColumns
21 )
22{
23 HRESULT hr = S_OK;
24 DWORD er;
25 LPWSTR sczSearch = NULL;
26 LPWSTR sczProperty = NULL;
27 HANDLE hFind = INVALID_HANDLE_VALUE;
28 WIN32_FIND_DATAW wfd;
29 LPWSTR sczNext = NULL;
30
31 if (fDisableWow64Redirection)
32 {
33 hr = WcaDisableWow64FSRedirection();
34 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
35 }
36
37 // First recurse down to all the child directories.
38 hr = StrAllocFormatted(&sczSearch, L"%s*", wzPath);
39 ExitOnFailure(hr, "Failed to allocate file search string in path: %S", wzPath);
40
41 hFind = ::FindFirstFileW(sczSearch, &wfd);
42 if (INVALID_HANDLE_VALUE == hFind)
43 {
44 er = ::GetLastError();
45 if (ERROR_PATH_NOT_FOUND == er)
46 {
47 WcaLog(LOGMSG_STANDARD, "Search path not found: %ls; skipping", sczSearch);
48 ExitFunction1(hr = S_FALSE);
49 }
50 else
51 {
52 hr = HRESULT_FROM_WIN32(er);
53 }
54 ExitOnFailure(hr, "Failed to find all files in path: %S", wzPath);
55 }
56
57 do
58 {
59 // Skip files and the dot directories.
60 if (FILE_ATTRIBUTE_DIRECTORY != (wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) || L'.' == wfd.cFileName[0] && (L'\0' == wfd.cFileName[1] || (L'.' == wfd.cFileName[1] && L'\0' == wfd.cFileName[2])))
61 {
62 continue;
63 }
64
65 hr = StrAllocFormatted(&sczNext, L"%s%s\\", wzPath, wfd.cFileName);
66 ExitOnFailure(hr, "Failed to concat filename '%S' to string: %S", wfd.cFileName, wzPath);
67
68 // Don't re-disable redirection; if it was necessary, we've already done it.
69 hr = RecursePath(sczNext, wzId, wzComponent, wzProperty, iMode, FALSE, pdwCounter, phTable, phColumns);
70 ExitOnFailure(hr, "Failed to recurse path: %S", sczNext);
71 } while (::FindNextFileW(hFind, &wfd));
72
73 er = ::GetLastError();
74 if (ERROR_NO_MORE_FILES == er)
75 {
76 hr = S_OK;
77 }
78 else
79 {
80 hr = HRESULT_FROM_WIN32(er);
81 ExitOnFailure(hr, "Failed while looping through files in directory: %S", wzPath);
82 }
83
84 // Finally, set a property that points at our path.
85 hr = StrAllocFormatted(&sczProperty, L"_%s_%u", wzProperty, *pdwCounter);
86 ExitOnFailure(hr, "Failed to allocate Property for RemoveFile table with property: %S.", wzProperty);
87
88 ++(*pdwCounter);
89
90 hr = WcaSetProperty(sczProperty, wzPath);
91 ExitOnFailure(hr, "Failed to set Property: %S with path: %S", sczProperty, wzPath);
92
93 // Add the row to remove any files and another row to remove the folder.
94 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFiles", wzComponent, L"*.*", sczProperty, iMode);
95 ExitOnFailure(hr, "Failed to add row to remove all files for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
96
97 hr = WcaAddTempRecord(phTable, phColumns, L"RemoveFile", NULL, 1, 5, L"RfxFolder", wzComponent, NULL, sczProperty, iMode);
98 ExitOnFailure(hr, "Failed to add row to remove folder for Wix4RemoveFolderEx row: %ls under path: %ls", wzId, wzPath);
99
100LExit:
101 if (INVALID_HANDLE_VALUE != hFind)
102 {
103 ::FindClose(hFind);
104 }
105
106 if (fDisableWow64Redirection)
107 {
108 WcaRevertWow64FSRedirection();
109 }
110
111 ReleaseStr(sczNext);
112 ReleaseStr(sczProperty);
113 ReleaseStr(sczSearch);
114 return hr;
115}
116
117extern "C" UINT WINAPI WixRemoveFoldersEx(
118 __in MSIHANDLE hInstall
119 )
120{
121 //AssertSz(FALSE, "debug WixRemoveFoldersEx");
122
123 HRESULT hr = S_OK;
124 PMSIHANDLE hView;
125 PMSIHANDLE hRec;
126 LPWSTR sczId = NULL;
127 LPWSTR sczComponent = NULL;
128 LPWSTR sczProperty = NULL;
129 LPWSTR sczCondition = NULL;
130 LPWSTR sczPath = NULL;
131 LPWSTR sczExpandedPath = NULL;
132 int iMode = 0;
133 int iComponentAttributes;
134 BOOL f64BitComponent = FALSE;
135 DWORD dwCounter = 0;
136 DWORD_PTR cchLen = 0;
137 MSIHANDLE hTable = NULL;
138 MSIHANDLE hColumns = NULL;
139
140 hr = WcaInitialize(hInstall, "WixRemoveFoldersEx");
141 ExitOnFailure(hr, "Failed to initialize WixRemoveFoldersEx.");
142
143 WcaInitializeWow64();
144
145 // anything to do?
146 if (S_OK != WcaTableExists(L"Wix4RemoveFolderEx"))
147 {
148 WcaLog(LOGMSG_STANDARD, "Wix4RemoveFolderEx table doesn't exist, so there are no folders to remove.");
149 ExitFunction();
150 }
151
152 // query and loop through all the remove folders exceptions
153 hr = WcaOpenExecuteView(vcsRemoveFolderExQuery, &hView);
154 ExitOnFailure(hr, "Failed to open view on Wix4RemoveFolderEx table");
155
156 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
157 {
158 hr = WcaGetRecordString(hRec, rfqId, &sczId);
159 ExitOnFailure(hr, "Failed to get remove folder identity.");
160
161 hr = WcaGetRecordString(hRec, rfqCondition, &sczCondition);
162 ExitOnFailure(hr, "Failed to get remove folder condition.");
163
164 if (sczCondition && *sczCondition)
165 {
166 MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition);
167 if (MSICONDITION_TRUE == condition)
168 {
169 WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition);
170 }
171 else
172 {
173 WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition);
174 continue;
175 }
176 }
177
178 hr = WcaGetRecordString(hRec, rfqComponent, &sczComponent);
179 ExitOnFailure(hr, "Failed to get remove folder component.");
180
181 hr = WcaGetRecordString(hRec, rfqProperty, &sczProperty);
182 ExitOnFailure(hr, "Failed to get remove folder property.");
183
184 hr = WcaGetRecordInteger(hRec, rfqMode, &iMode);
185 ExitOnFailure(hr, "Failed to get remove folder mode");
186
187 hr = WcaGetProperty(sczProperty, &sczPath);
188 ExitOnFailure(hr, "Failed to resolve remove folder property: %S for row: %S", sczProperty, sczId);
189
190 hr = WcaGetRecordInteger(hRec, rfqComponentAttributes, &iComponentAttributes);
191 ExitOnFailure(hr, "failed to get component attributes for row: %ls", sczId);
192
193 f64BitComponent = iComponentAttributes & msidbComponentAttributes64bit;
194
195 // fail early if the property isn't set as you probably don't want your installers trying to delete SystemFolder
196 // StringCchLengthW succeeds only if the string is zero characters plus 1 for the terminating null
197 hr = ::StringCchLengthW(sczPath, 1, reinterpret_cast<UINT_PTR*>(&cchLen));
198 if (SUCCEEDED(hr))
199 {
200 ExitOnFailure(hr = E_INVALIDARG, "Missing folder property: %S for row: %S", sczProperty, sczId);
201 }
202
203 hr = PathExpand(&sczExpandedPath, sczPath, PATH_EXPAND_ENVIRONMENT);
204 ExitOnFailure(hr, "Failed to expand path: %S for row: %S", sczPath, sczId);
205
206 hr = PathBackslashTerminate(&sczExpandedPath);
207 ExitOnFailure(hr, "Failed to backslash-terminate path: %S", sczExpandedPath);
208
209 WcaLog(LOGMSG_STANDARD, "Recursing path: %S for row: %S.", sczExpandedPath, sczId);
210 hr = RecursePath(sczExpandedPath, sczId, sczComponent, sczProperty, iMode, f64BitComponent, &dwCounter, &hTable, &hColumns);
211 ExitOnFailure(hr, "Failed while navigating path: %S for row: %S", sczPath, sczId);
212 }
213
214 // reaching the end of the list is actually a good thing, not an error
215 if (E_NOMOREITEMS == hr)
216 {
217 hr = S_OK;
218 }
219 ExitOnFailure(hr, "Failure occured while processing Wix4RemoveFolderEx table");
220
221LExit:
222 WcaFinalizeWow64();
223
224 if (hColumns)
225 {
226 ::MsiCloseHandle(hColumns);
227 }
228
229 if (hTable)
230 {
231 ::MsiCloseHandle(hTable);
232 }
233
234 ReleaseStr(sczExpandedPath);
235 ReleaseStr(sczPath);
236 ReleaseStr(sczProperty);
237 ReleaseStr(sczComponent);
238 ReleaseStr(sczCondition);
239 ReleaseStr(sczId);
240
241 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
242 return WcaFinalize(er);
243}
diff --git a/src/ext/Util/ca/RemoveRegistryKeysEx.cpp b/src/ext/Util/ca/RemoveRegistryKeysEx.cpp
new file mode 100644
index 00000000..478c0779
--- /dev/null
+++ b/src/ext/Util/ca/RemoveRegistryKeysEx.cpp
@@ -0,0 +1,114 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsRemoveRegistryKeyExQuery =
6 L"SELECT `Wix4RemoveRegistryKeyEx`, `Component_`, `Root`, `Key`, `InstallMode`, `Condition` FROM `Wix4RemoveRegistryKeyEx`";
7enum eRemoveRegistryKeyExQuery { rrxqId = 1, rrxqComponent, rrxqRoot, rrxqKey, rrxqMode, rrxqCondition };
8
9extern "C" UINT WINAPI WixRemoveRegistryKeysEx(
10 __in MSIHANDLE hInstall
11)
12{
13 //AssertSz(FALSE, "debug WixRemoveRegistryKeyEx");
14
15 HRESULT hr = S_OK;
16 PMSIHANDLE hView;
17 PMSIHANDLE hRec;
18 LPWSTR sczId = NULL;
19 LPWSTR sczComponent = NULL;
20 LPWSTR sczCondition = NULL;
21 LPWSTR sczKey = NULL;
22 int iRoot = 0;
23 int iMode = 0;
24 MSIHANDLE hTable = NULL;
25 MSIHANDLE hColumns = NULL;
26
27 hr = WcaInitialize(hInstall, __FUNCTION__);
28 ExitOnFailure(hr, "Failed to initialize " __FUNCTION__);
29
30 // anything to do?
31 if (S_OK != WcaTableExists(L"Wix4RemoveRegistryKeyEx"))
32 {
33 WcaLog(LOGMSG_STANDARD, "Wix4RemoveRegistryKeyEx table doesn't exist, so there are no registry keys to remove.");
34 ExitFunction();
35 }
36
37 hr = WcaOpenExecuteView(vcsRemoveRegistryKeyExQuery, &hView);
38 ExitOnFailure(hr, "Failed to open view on Wix4RemoveRegistryKeyEx table");
39
40 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
41 {
42 hr = WcaGetRecordString(hRec, rrxqId, &sczId);
43 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx identity.");
44
45 hr = WcaGetRecordString(hRec, rrxqCondition, &sczCondition);
46 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx condition.");
47
48 if (sczCondition && *sczCondition)
49 {
50 MSICONDITION condition = ::MsiEvaluateConditionW(hInstall, sczCondition);
51 if (MSICONDITION_TRUE == condition)
52 {
53 WcaLog(LOGMSG_STANDARD, "True condition for row %S: %S; processing.", sczId, sczCondition);
54 }
55 else
56 {
57 WcaLog(LOGMSG_STANDARD, "False or invalid condition for row %S: %S; skipping.", sczId, sczCondition);
58 continue;
59 }
60 }
61
62 hr = WcaGetRecordString(hRec, rrxqComponent, &sczComponent);
63 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx component.");
64
65 hr = WcaGetRecordInteger(hRec, rrxqRoot, &iRoot);
66 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx root.");
67
68 hr = WcaGetRecordString(hRec, rrxqKey, &sczKey);
69 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx key.");
70
71 hr = WcaGetRecordInteger(hRec, rrxqMode, &iMode);
72 ExitOnFailure(hr, "Failed to get Wix4RemoveRegistryKeyEx mode.");
73
74 switch (iMode)
75 {
76 case 1: // remove on install
77 WcaLog(LOGMSG_STANDARD, "Adding RemoveRegistry row: %ls/%d/%ls/-/%ls", sczId, iRoot, sczKey, sczComponent);
78 hr = WcaAddTempRecord(&hTable, &hColumns, L"RemoveRegistry", NULL, 0, 5, sczId, iRoot, sczKey, L"-", sczComponent);
79 ExitOnFailure(hr, "Failed to add RemoveRegistry row for remove-on-install Wix4RemoveRegistryKeyEx row: %ls:", sczId);
80 break;
81 case 2: // remove on uninstall
82 WcaLog(LOGMSG_STANDARD, "Adding Registry row: %ls/%d/%ls/-/null/%ls", sczId, iRoot, sczKey, sczComponent);
83 hr = WcaAddTempRecord(&hTable, &hColumns, L"Registry", NULL, 0, 6, sczId, iRoot, sczKey, L"-", NULL, sczComponent);
84 ExitOnFailure(hr, "Failed to add Registry row for remove-on-uninstall Wix4RemoveRegistryKeyEx row: %ls:", sczId);
85 break;
86 }
87 }
88
89 // reaching the end of the list is actually a good thing, not an error
90 if (E_NOMOREITEMS == hr)
91 {
92 hr = S_OK;
93 }
94 ExitOnFailure(hr, "Failure occured while processing Wix4RemoveRegistryKeyEx table.");
95
96LExit:
97 if (hColumns)
98 {
99 ::MsiCloseHandle(hColumns);
100 }
101
102 if (hTable)
103 {
104 ::MsiCloseHandle(hTable);
105 }
106
107 ReleaseStr(sczKey);
108 ReleaseStr(sczComponent);
109 ReleaseStr(sczCondition);
110 ReleaseStr(sczId);
111
112 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
113 return WcaFinalize(er);
114}
diff --git a/src/ext/Util/ca/RestartManager.cpp b/src/ext/Util/ca/RestartManager.cpp
new file mode 100644
index 00000000..c31819c1
--- /dev/null
+++ b/src/ext/Util/ca/RestartManager.cpp
@@ -0,0 +1,185 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4#include <restartmanager.h>
5
6// Include space for the terminating null.
7#define CCH_SESSION_KEY CCH_RM_SESSION_KEY + 1
8
9enum eRmuResourceType
10{
11 etInvalid,
12 etFilename,
13 etApplication,
14 etServiceName,
15
16 // Mask types from Attributes.
17 etTypeMask = 0xf,
18};
19
20LPCWSTR vcsRestartResourceQuery =
21 L"SELECT `Wix4RestartResource`.`Wix4RestartResource`, `Wix4RestartResource`.`Component_`, `Wix4RestartResource`.`Resource`, `Wix4RestartResource`.`Attributes` "
22 L"FROM `Wix4RestartResource`";
23enum eRestartResourceQuery { rrqRestartResource = 1, rrqComponent, rrqResource, rrqAttributes };
24
25/********************************************************************
26WixRegisterRestartResources - Immediate CA to register resources with RM.
27
28Enumerates components before InstallValidate and registers resources
29to be restarted by Restart Manager if the component action
30is anything other than None.
31
32Do not disable file system redirection.
33
34********************************************************************/
35extern "C" UINT __stdcall WixRegisterRestartResources(
36 __in MSIHANDLE hInstall
37 )
38{
39 HRESULT hr = S_OK;
40 UINT er = ERROR_SUCCESS;
41
42 PMSIHANDLE hView = NULL;
43 PMSIHANDLE hRec = NULL;
44
45 LPWSTR wzSessionKey = NULL;
46 size_t cchSessionKey = 0;
47 PRMU_SESSION pSession = NULL;
48
49 LPWSTR wzRestartResource = NULL;
50 LPWSTR wzComponent = NULL;
51 LPWSTR wzResource = NULL;
52 int iAttributes = NULL;
53 BOOL fIsComponentNull = FALSE;
54 WCA_TODO todo = WCA_TODO_UNKNOWN;
55 int iType = etInvalid;
56
57 hr = WcaInitialize(hInstall, "WixRegisterRestartResources");
58 ExitOnFailure(hr, "Failed to initialize.");
59
60 // Skip if the table doesn't exist.
61 if (S_OK != WcaTableExists(L"Wix4RestartResource"))
62 {
63 WcaLog(LOGMSG_STANDARD, "The Wix4RestartResource table does not exist; there are no resources to register with Restart Manager.");
64 ExitFunction();
65 }
66
67 // Get the existing Restart Manager session if available.
68 hr = WcaGetProperty(L"MsiRestartManagerSessionKey", &wzSessionKey);
69 ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey property.");
70
71 hr = ::StringCchLengthW(wzSessionKey, CCH_SESSION_KEY, &cchSessionKey);
72 ExitOnFailure(hr, "Failed to get the MsiRestartManagerSessionKey string length.");
73
74 // Skip if the property doesn't exist.
75 if (0 == cchSessionKey)
76 {
77 WcaLog(LOGMSG_STANDARD, "The MsiRestartManagerSessionKey property is not available to join.");
78 ExitFunction();
79 }
80
81 // Join the existing Restart Manager session if supported.
82 hr = RmuJoinSession(&pSession, wzSessionKey);
83 if (E_MODNOTFOUND == hr)
84 {
85 WcaLog(LOGMSG_STANDARD, "The Restart Manager is not supported on this platform. Skipping.");
86 ExitFunction1(hr = S_OK);
87 }
88 else if (FAILED(hr))
89 {
90 WcaLog(LOGMSG_STANDARD, "Failed to join the existing Restart Manager session %ls.", wzSessionKey);
91 ExitFunction1(hr = S_OK);
92 }
93
94 // Loop through each record in the table.
95 hr = WcaOpenExecuteView(vcsRestartResourceQuery, &hView);
96 ExitOnFailure(hr, "Failed to open a view on the RestartResource table.");
97
98 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
99 {
100 hr = WcaGetRecordString(hRec, rrqRestartResource, &wzRestartResource);
101 ExitOnFailure(hr, "Failed to get the RestartResource field value.");
102
103 hr = WcaGetRecordString(hRec, rrqComponent, &wzComponent);
104 ExitOnFailure(hr, "Failed to get the Component_ field value.");
105
106 hr = WcaGetRecordFormattedString(hRec, rrqResource, &wzResource);
107 ExitOnFailure(hr, "Failed to get the Resource formatted field value.");
108
109 hr = WcaGetRecordInteger(hRec, rrqAttributes, &iAttributes);
110 ExitOnFailure(hr, "Failed to get the Attributes field value.");
111
112 fIsComponentNull = ::MsiRecordIsNull(hRec, rrqComponent);
113 todo = WcaGetComponentToDo(wzComponent);
114
115 // Only register resources for components that are null, or being installed, reinstalled, or uninstalled.
116 if (!fIsComponentNull && WCA_TODO_UNKNOWN == todo)
117 {
118 WcaLog(LOGMSG_VERBOSE, "Skipping resource %ls.", wzRestartResource);
119 continue;
120 }
121
122 // Get the type from Attributes and add to the Restart Manager.
123 iType = iAttributes & etTypeMask;
124 switch (iType)
125 {
126 case etFilename:
127 WcaLog(LOGMSG_VERBOSE, "Registering file name %ls with the Restart Manager.", wzResource);
128 hr = RmuAddFile(pSession, wzResource);
129 ExitOnFailure(hr, "Failed to register the file name with the Restart Manager session.");
130 break;
131
132 case etApplication:
133 WcaLog(LOGMSG_VERBOSE, "Registering process name %ls with the Restart Manager.", wzResource);
134 hr = RmuAddProcessesByName(pSession, wzResource);
135 if (E_NOTFOUND == hr)
136 {
137 // ERROR_ACCESS_DENIED was returned when trying to register this process.
138 // Since other instances may have been registered, log a message and continue the setup rather than failing.
139 WcaLog(LOGMSG_STANDARD, "The process, %ls, could not be registered with the Restart Manager (probably because the setup is not elevated and the process is in another user context). A reboot may be requested later.", wzResource);
140 hr = S_OK;
141 }
142 else
143 {
144 ExitOnFailure(hr, "Failed to register the process name with the Restart Manager session.");
145 }
146 break;
147
148 case etServiceName:
149 WcaLog(LOGMSG_VERBOSE, "Registering service name %ls with the Restart Manager.", wzResource);
150 hr = RmuAddService(pSession, wzResource);
151 ExitOnFailure(hr, "Failed to register the service name with the Restart Manager session.");
152 break;
153
154 default:
155 WcaLog(LOGMSG_VERBOSE, "The resource type %d for %ls is not supported and will not be registered.", iType, wzRestartResource);
156 break;
157 }
158 }
159
160 if (E_NOMOREITEMS == hr)
161 {
162 hr = S_OK;
163 }
164 ExitOnFailure(hr, "Failed while looping through all rows to register resources.");
165
166 // Register the resources and unjoin the session.
167 hr = RmuEndSession(pSession);
168 if (FAILED(hr))
169 {
170 WcaLog(LOGMSG_VERBOSE, "Failed to register the resources with the Restart Manager.");
171 ExitFunction1(hr = S_OK);
172 }
173
174LExit:
175 ReleaseStr(wzRestartResource);
176 ReleaseStr(wzComponent);
177 ReleaseStr(wzResource);
178
179 if (FAILED(hr))
180 {
181 er = ERROR_INSTALL_FAILURE;
182 }
183
184 return WcaFinalize(er);
185}
diff --git a/src/ext/Util/ca/TouchFile.cpp b/src/ext/Util/ca/TouchFile.cpp
new file mode 100644
index 00000000..e704f922
--- /dev/null
+++ b/src/ext/Util/ca/TouchFile.cpp
@@ -0,0 +1,308 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsTouchFileQuery = L"SELECT `Wix4TouchFile`, `Component_`, `Path`, `Attributes` FROM `Wix4TouchFile`";
6enum TOUCH_FILE_QUERY { tfqId = 1, tfqComponent, tfqPath, tfqTouchFileAttributes };
7
8enum TOUCH_FILE_ATTRIBUTE
9{
10 TOUCH_FILE_ATTRIBUTE_ON_INSTALL = 0x01,
11 TOUCH_FILE_ATTRIBUTE_ON_REINSTALL = 0x02,
12 TOUCH_FILE_ATTRIBUTE_ON_UNINSTALL = 0x04,
13 TOUCH_FILE_ATTRIBUTE_64BIT = 0x10,
14 TOUCH_FILE_ATTRIBUTE_VITAL = 0x20
15};
16
17
18static BOOL SetExistingFileModifiedTime(
19 __in_z LPCWSTR wzId,
20 __in_z LPCWSTR wzPath,
21 __in BOOL f64Bit,
22 __in FILETIME* pftModified
23 )
24{
25 HRESULT hr = S_OK;
26 BOOL fReenableFileSystemRedirection = FALSE;
27
28 if (f64Bit)
29 {
30 hr = WcaDisableWow64FSRedirection();
31 ExitOnFailure(hr, "Failed to disable 64-bit file system redirection to path: '%ls' for: %ls", wzPath, wzId);
32
33 fReenableFileSystemRedirection = TRUE;
34 }
35
36 hr = FileSetTime(wzPath, NULL, NULL, pftModified);
37
38LExit:
39 if (fReenableFileSystemRedirection)
40 {
41 WcaRevertWow64FSRedirection();
42 }
43
44 return SUCCEEDED(hr);
45}
46
47
48static HRESULT AddDataToCustomActionData(
49 __deref_inout_z LPWSTR* psczCustomActionData,
50 __in_z LPCWSTR wzId,
51 __in_z LPCWSTR wzPath,
52 __in int iTouchFileAttributes,
53 __in FILETIME ftModified
54 )
55{
56 HRESULT hr = S_OK;
57
58 hr = WcaWriteStringToCaData(wzId, psczCustomActionData);
59 ExitOnFailure(hr, "Failed to add touch file identity to custom action data.");
60
61 hr = WcaWriteStringToCaData(wzPath, psczCustomActionData);
62 ExitOnFailure(hr, "Failed to add touch file path to custom action data.");
63
64 hr = WcaWriteIntegerToCaData(iTouchFileAttributes, psczCustomActionData);
65 ExitOnFailure(hr, "Failed to add touch file attributes to custom action data.");
66
67 hr = WcaWriteIntegerToCaData(ftModified.dwHighDateTime, psczCustomActionData);
68 ExitOnFailure(hr, "Failed to add touch file high date/time to custom action data.");
69
70 hr = WcaWriteIntegerToCaData(ftModified.dwLowDateTime, psczCustomActionData);
71 ExitOnFailure(hr, "Failed to add touch file low date/time to custom action data.");
72
73LExit:
74 return hr;
75}
76
77
78static BOOL TryGetExistingFileModifiedTime(
79 __in_z LPCWSTR wzId,
80 __in_z LPCWSTR wzPath,
81 __in BOOL f64Bit,
82 __inout FILETIME* pftModified
83 )
84{
85 HRESULT hr = S_OK;
86 BOOL fReenableFileSystemRedirection = FALSE;
87
88 if (f64Bit)
89 {
90 hr = WcaDisableWow64FSRedirection();
91 ExitOnFailure(hr, "Failed to disable 64-bit file system redirection to path: '%ls' for: %ls", wzPath, wzId);
92
93 fReenableFileSystemRedirection = TRUE;
94 }
95
96 hr = FileGetTime(wzPath, NULL, NULL, pftModified);
97 if (E_PATHNOTFOUND == hr || E_FILENOTFOUND == hr)
98 {
99 // If the file doesn't exist yet there is nothing to rollback (i.e. file will probably be removed during rollback), so
100 // keep the error code but don't log anything.
101 }
102 else if (FAILED(hr))
103 {
104 WcaLog(LOGMSG_STANDARD, "Cannot access modified timestamp for file: '%ls' due to error: 0x%x. Continuing with out rollback for: %ls", wzPath, hr, wzId);
105 }
106
107LExit:
108 if (fReenableFileSystemRedirection)
109 {
110 WcaRevertWow64FSRedirection();
111 }
112
113 return SUCCEEDED(hr);
114}
115
116
117static HRESULT ProcessTouchFileTable(
118 __in BOOL fInstalling
119 )
120{
121 HRESULT hr = S_OK;
122
123 FILETIME ftModified = {};
124
125 PMSIHANDLE hView;
126 PMSIHANDLE hRec;
127
128 LPWSTR sczId = NULL;
129 LPWSTR sczComponent = NULL;
130 int iTouchFileAttributes = 0;
131 LPWSTR sczPath = NULL;
132
133 FILETIME ftRollbackModified = {};
134 LPWSTR sczRollbackData = NULL;
135 LPWSTR sczExecuteData = NULL;
136
137 if (S_OK != WcaTableExists(L"Wix4TouchFile"))
138 {
139 ExitFunction();
140 }
141
142 ::GetSystemTimeAsFileTime(&ftModified);
143
144 hr = WcaOpenExecuteView(vcsTouchFileQuery, &hView);
145 ExitOnFailure(hr, "Failed to open view on Wix4TouchFile table");
146
147 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
148 {
149 hr = WcaGetRecordString(hRec, tfqId, &sczId);
150 ExitOnFailure(hr, "Failed to get touch file identity.");
151
152 hr = WcaGetRecordString(hRec, tfqComponent, &sczComponent);
153 ExitOnFailure(hr, "Failed to get touch file component for: %ls", sczId);
154
155 hr = WcaGetRecordInteger(hRec, tfqTouchFileAttributes, &iTouchFileAttributes);
156 ExitOnFailure(hr, "Failed to get touch file attributes for: %ls", sczId);
157
158 WCA_TODO todo = WcaGetComponentToDo(sczComponent);
159
160 BOOL fOnInstall = fInstalling && WCA_TODO_INSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_INSTALL);
161 BOOL fOnReinstall = fInstalling && WCA_TODO_REINSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_REINSTALL);
162 BOOL fOnUninstall = !fInstalling && WCA_TODO_UNINSTALL == todo && (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_ON_UNINSTALL);
163
164 if (fOnInstall || fOnReinstall || fOnUninstall)
165 {
166 hr = WcaGetRecordFormattedString(hRec, tfqPath, &sczPath);
167 ExitOnFailure(hr, "Failed to get touch file path for: %ls", sczId);
168
169 if (TryGetExistingFileModifiedTime(sczId, sczPath, (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_64BIT), &ftRollbackModified))
170 {
171 hr = AddDataToCustomActionData(&sczRollbackData, sczId, sczPath, iTouchFileAttributes, ftRollbackModified);
172 ExitOnFailure(hr, "Failed to add to rollback custom action data for: %ls", sczId);
173 }
174
175 hr = AddDataToCustomActionData(&sczExecuteData, sczId, sczPath, iTouchFileAttributes, ftModified);
176 ExitOnFailure(hr, "Failed to add to execute custom action data for: %ls", sczId);
177 }
178 }
179
180 if (E_NOMOREITEMS == hr)
181 {
182 hr = S_OK;
183 }
184 ExitOnFailure(hr, "Failure occured while processing Wix4TouchFile table");
185
186 if (sczRollbackData)
187 {
188 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackTouchFile"), sczRollbackData, 0);
189 ExitOnFailure(hr, "Failed to schedule RollbackTouchFile");
190 }
191
192 if (sczExecuteData)
193 {
194 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecuteTouchFile"), sczExecuteData, 0);
195 ExitOnFailure(hr, "Failed to schedule ExecuteTouchFile");
196 }
197
198LExit:
199 ReleaseStr(sczExecuteData);
200 ReleaseStr(sczRollbackData);
201 ReleaseStr(sczPath);
202 ReleaseStr(sczComponent);
203 ReleaseStr(sczId);
204
205 return hr;
206}
207
208
209extern "C" UINT WINAPI WixTouchFileDuringInstall(
210 __in MSIHANDLE hInstall
211 )
212{
213 //AssertSz(FALSE, "debug WixTouchFileDuringInstall");
214
215 HRESULT hr = S_OK;
216
217 hr = WcaInitialize(hInstall, "WixTouchFileDuringInstall");
218 ExitOnFailure(hr, "Failed to initialize WixTouchFileDuringInstall.");
219
220 hr = ProcessTouchFileTable(TRUE);
221
222LExit:
223 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
224 return WcaFinalize(er);
225}
226
227
228extern "C" UINT WINAPI WixTouchFileDuringUninstall(
229 __in MSIHANDLE hInstall
230 )
231{
232 //AssertSz(FALSE, "debug WixTouchFileDuringUninstall");
233
234 HRESULT hr = S_OK;
235
236 hr = WcaInitialize(hInstall, "WixTouchFileDuringUninstall");
237 ExitOnFailure(hr, "Failed to initialize WixTouchFileDuringUninstall.");
238
239 hr = ProcessTouchFileTable(FALSE);
240
241LExit:
242 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
243 return WcaFinalize(er);
244}
245
246
247extern "C" UINT WINAPI WixExecuteTouchFile(
248 __in MSIHANDLE hInstall
249 )
250{
251 HRESULT hr = S_OK;
252
253 LPWSTR sczData = NULL;
254 LPWSTR pwz = NULL;
255
256 LPWSTR sczId = NULL;
257 LPWSTR sczPath = NULL;
258 int iTouchFileAttributes = 0;
259 FILETIME ftModified = {};
260
261 hr = WcaInitialize(hInstall, "WixExecuteTouchFile");
262 ExitOnFailure(hr, "Failed to initialize WixExecuteTouchFile.");
263
264 hr = WcaGetProperty(L"CustomActionData", &sczData);
265 ExitOnFailure(hr, "Failed to get custom action data for WixExecuteTouchFile.");
266
267 pwz = sczData;
268
269 while (pwz && *pwz)
270 {
271 hr = WcaReadStringFromCaData(&pwz, &sczId);
272 ExitOnFailure(hr, "Failed to get touch file identity from custom action data.");
273
274 hr = WcaReadStringFromCaData(&pwz, &sczPath);
275 ExitOnFailure(hr, "Failed to get touch file path from custom action data for: %ls", sczId);
276
277 hr = WcaReadIntegerFromCaData(&pwz, &iTouchFileAttributes);
278 ExitOnFailure(hr, "Failed to get touch file attributes from custom action data for: %ls", sczId);
279
280 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&ftModified.dwHighDateTime));
281 ExitOnFailure(hr, "Failed to get touch file high date/time from custom action data for: %ls", sczId);
282
283 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&ftModified.dwLowDateTime));
284 ExitOnFailure(hr, "Failed to get touch file low date/time from custom action data for: %ls", sczId);
285
286 hr = SetExistingFileModifiedTime(sczId, sczPath, (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_64BIT), &ftModified);
287 if (FAILED(hr))
288 {
289 if (iTouchFileAttributes & TOUCH_FILE_ATTRIBUTE_VITAL)
290 {
291 ExitOnFailure(hr, "Failed to touch file: '%ls' for: %ls", &sczPath, sczId);
292 }
293 else
294 {
295 WcaLog(LOGMSG_STANDARD, "Could not touch non-vital file: '%ls' for: %ls with error: 0x%x. Continuing...", sczPath, sczId, hr);
296 hr = S_OK;
297 }
298 }
299 }
300
301LExit:
302 ReleaseStr(sczPath);
303 ReleaseStr(sczId);
304 ReleaseStr(sczData);
305
306 DWORD er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
307 return WcaFinalize(er);
308}
diff --git a/src/ext/Util/ca/XmlConfig.cpp b/src/ext/Util/ca/XmlConfig.cpp
new file mode 100644
index 00000000..a1ec9d6f
--- /dev/null
+++ b/src/ext/Util/ca/XmlConfig.cpp
@@ -0,0 +1,1130 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define XMLCONFIG_ELEMENT 0x00000001
6#define XMLCONFIG_VALUE 0x00000002
7#define XMLCONFIG_DOCUMENT 0x00000004
8#define XMLCONFIG_CREATE 0x00000010
9#define XMLCONFIG_DELETE 0x00000020
10#define XMLCONFIG_INSTALL 0x00000100
11#define XMLCONFIG_UNINSTALL 0x00000200
12#define XMLCONFIG_PRESERVE_MODIFIED 0x00001000
13
14enum eXmlAction
15{
16 xaUnknown = 0,
17 xaOpenFile,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaWriteDocument,
21 xaDeleteValue,
22 xaCreateElement,
23 xaDeleteElement,
24};
25
26enum eXmlPreserveDate
27{
28 xdDontPreserve = 0,
29 xdPreserve
30};
31
32LPCWSTR vcsXmlConfigQuery =
33 L"SELECT `Wix4XmlConfig`.`Wix4XmlConfig`, `Wix4XmlConfig`.`File`, `Wix4XmlConfig`.`ElementId`, `Wix4XmlConfig`.`ElementPath`, `Wix4XmlConfig`.`VerifyPath`, `Wix4XmlConfig`.`Name`, "
34 L"`Wix4XmlConfig`.`Value`, `Wix4XmlConfig`.`Flags`, `Wix4XmlConfig`.`Component_`, `Component`.`Attributes` "
35 L"FROM `Wix4XmlConfig`,`Component` WHERE `Wix4XmlConfig`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
36enum eXmlConfigQuery { xfqXmlConfig = 1, xfqFile, xfqElementId, xfqElementPath, xfqVerifyPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
37
38struct XML_CONFIG_CHANGE
39{
40 WCHAR wzId[MAX_DARWIN_KEY + 1];
41
42 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
43 INSTALLSTATE isInstalled;
44 INSTALLSTATE isAction;
45
46 WCHAR wzFile[MAX_PATH];
47 LPWSTR pwzElementId;
48 LPWSTR pwzElementPath;
49 LPWSTR pwzVerifyPath;
50 WCHAR wzName[MAX_DARWIN_COLUMN];
51 LPWSTR pwzValue;
52 BOOL fInstalledFile;
53
54 int iXmlFlags;
55 int iCompAttributes;
56
57 XML_CONFIG_CHANGE* pxfcAdditionalChanges;
58 int cAdditionalChanges;
59
60 XML_CONFIG_CHANGE* pxfcPrev;
61 XML_CONFIG_CHANGE* pxfcNext;
62};
63
64static HRESULT FreeXmlConfigChangeList(
65 __in_opt XML_CONFIG_CHANGE* pxfcList
66 )
67{
68 HRESULT hr = S_OK;
69
70 XML_CONFIG_CHANGE* pxfcDelete;
71 while(pxfcList)
72 {
73 pxfcDelete = pxfcList;
74 pxfcList = pxfcList->pxfcNext;
75
76 if (pxfcDelete->pwzElementId)
77 {
78 hr = MemFree(pxfcDelete->pwzElementId);
79 ExitOnFailure(hr, "failed to free xml config element id in change list item");
80 }
81
82 if (pxfcDelete->pwzElementPath)
83 {
84 hr = MemFree(pxfcDelete->pwzElementPath);
85 ExitOnFailure(hr, "failed to free xml config element path in change list item");
86 }
87
88 if (pxfcDelete->pwzVerifyPath)
89 {
90 hr = MemFree(pxfcDelete->pwzVerifyPath);
91 ExitOnFailure(hr, "failed to free xml config verify path in change list item");
92 }
93
94 if (pxfcDelete->pwzValue)
95 {
96 hr = MemFree(pxfcDelete->pwzValue);
97 ExitOnFailure(hr, "failed to free xml config value in change list item");
98 }
99
100 hr = MemFree(pxfcDelete);
101 ExitOnFailure(hr, "failed to free xml config change list item");
102 }
103
104LExit:
105 return hr;
106}
107
108static HRESULT AddXmlConfigChangeToList(
109 __inout XML_CONFIG_CHANGE** ppxfcHead,
110 __inout XML_CONFIG_CHANGE** ppxfcTail
111 )
112{
113 Assert(ppxfcHead && ppxfcTail);
114
115 HRESULT hr = S_OK;
116
117 XML_CONFIG_CHANGE* pxfc = static_cast<XML_CONFIG_CHANGE*>(MemAlloc(sizeof(XML_CONFIG_CHANGE), TRUE));
118 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
119
120 // Add it to the end of the list
121 if (NULL == *ppxfcHead)
122 {
123 *ppxfcHead = pxfc;
124 *ppxfcTail = pxfc;
125 }
126 else
127 {
128 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
129 (*ppxfcTail)->pxfcNext = pxfc;
130 pxfc->pxfcPrev = *ppxfcTail;
131 *ppxfcTail = pxfc;
132 }
133
134LExit:
135 return hr;
136}
137
138
139static HRESULT ReadXmlConfigTable(
140 __inout XML_CONFIG_CHANGE** ppxfcHead,
141 __inout XML_CONFIG_CHANGE** ppxfcTail
142 )
143{
144 Assert(ppxfcHead && ppxfcTail);
145
146 HRESULT hr = S_OK;
147 UINT er = ERROR_SUCCESS;
148
149 PMSIHANDLE hView = NULL;
150 PMSIHANDLE hRec = NULL;
151
152 LPWSTR pwzData = NULL;
153
154 // loop through all the xml configurations
155 hr = WcaOpenExecuteView(vcsXmlConfigQuery, &hView);
156 ExitOnFailure(hr, "failed to open view on Wix4XmlConfig table");
157
158 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
159 {
160 hr = AddXmlConfigChangeToList(ppxfcHead, ppxfcTail);
161 ExitOnFailure(hr, "failed to add xml file change to list");
162
163 // Get record Id
164 hr = WcaGetRecordString(hRec, xfqXmlConfig, &pwzData);
165 ExitOnFailure(hr, "failed to get Wix4XmlConfig record Id");
166 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
167 ExitOnFailure(hr, "failed to copy Wix4XmlConfig record Id");
168
169 // Get component name
170 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
171 ExitOnFailure(hr, "failed to get component name for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
172
173 // Get the component's state
174 if (pwzData && *pwzData)
175 {
176 hr = StringCchCopyW((*ppxfcTail)->wzComponent, countof((*ppxfcTail)->wzComponent), pwzData);
177 ExitOnFailure(hr, "failed to copy component id");
178
179 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), (*ppxfcTail)->wzComponent, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
180 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for component id");
181 }
182
183 // Get the xml file
184 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
185 ExitOnFailure(hr, "failed to get xml file for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
186 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
187 ExitOnFailure(hr, "failed to copy xml file path");
188
189 // Figure out if the file is already on the machine or if it's being installed
190 hr = WcaGetRecordString(hRec, xfqFile, &pwzData);
191 ExitOnFailure(hr, "failed to get xml file for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
192 if (NULL != wcsstr(pwzData, L"[!") || NULL != wcsstr(pwzData, L"[#"))
193 {
194 (*ppxfcTail)->fInstalledFile = TRUE;
195 }
196
197 // Get the Wix4XmlConfig table flags
198 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
199 ExitOnFailure(hr, "failed to get Wix4XmlConfig flags for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
200
201 // Get the Element Id
202 hr = WcaGetRecordFormattedString(hRec, xfqElementId, &(*ppxfcTail)->pwzElementId);
203 ExitOnFailure(hr, "failed to get Element Id for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
204
205 // Get the Element Path
206 hr = WcaGetRecordFormattedString(hRec, xfqElementPath, &(*ppxfcTail)->pwzElementPath);
207 ExitOnFailure(hr, "failed to get Element Path for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
208
209 // Get the Verify Path
210 hr = WcaGetRecordFormattedString(hRec, xfqVerifyPath, &(*ppxfcTail)->pwzVerifyPath);
211 ExitOnFailure(hr, "failed to get Verify Path for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
212
213 // Get the name
214 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
215 ExitOnFailure(hr, "failed to get Name for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
216 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
217 ExitOnFailure(hr, "failed to copy name of element");
218
219 // Get the value
220 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
221 ExitOnFailure(hr, "failed to get Value for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
222 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
223 ExitOnFailure(hr, "failed to allocate buffer for value");
224
225 // Get the component attributes
226 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
227 ExitOnFailure(hr, "failed to get component attributes for Wix4XmlConfig: %ls", (*ppxfcTail)->wzId);
228 }
229
230 // if we looped through all records all is well
231 if (E_NOMOREITEMS == hr)
232 {
233 hr = S_OK;
234 }
235 ExitOnFailure(hr, "failed while looping through all objects to secure");
236
237LExit:
238 ReleaseStr(pwzData);
239
240 return hr;
241}
242
243static HRESULT ProcessChanges(
244 __inout XML_CONFIG_CHANGE** ppxfcHead
245 )
246{
247 Assert(ppxfcHead && *ppxfcHead);
248 HRESULT hr = S_OK;
249
250 XML_CONFIG_CHANGE* pxfc = NULL;
251 XML_CONFIG_CHANGE* pxfcNext = NULL;
252 XML_CONFIG_CHANGE* pxfcCheck = NULL;
253 int cAdditionalChanges = 0;
254 XML_CONFIG_CHANGE* pxfcLast = NULL;
255
256 // If there's only one item in the list, none of this matters
257 if (pxfc && !pxfc->pxfcNext)
258 {
259 ExitFunction();
260 }
261
262 // Loop through the list
263 pxfc = *ppxfcHead;
264 while (pxfc)
265 {
266 // Keep track of where our next spot will be since our current node may be moved
267 pxfcNext = pxfc->pxfcNext;
268
269 // With each node, check to see if it's element path matches the Id of some other node in the list
270 pxfcCheck = *ppxfcHead;
271 while (pxfcCheck)
272 {
273 if (pxfc->pwzElementId)
274 {
275 if (0 == lstrcmpW(pxfc->pwzElementId, pxfcCheck->wzId)
276 && 0 == pxfc->iXmlFlags
277 && XMLCONFIG_CREATE & pxfcCheck->iXmlFlags
278 && XMLCONFIG_ELEMENT & pxfcCheck->iXmlFlags)
279 {
280 // We found a match. First, take it out of the current list
281 if (pxfc->pxfcPrev)
282 {
283 pxfc->pxfcPrev->pxfcNext = pxfc->pxfcNext;
284 }
285 else // it was the head. Update the head
286 {
287 *ppxfcHead = pxfc->pxfcNext;
288 }
289
290 if (pxfc->pxfcNext)
291 {
292 pxfc->pxfcNext->pxfcPrev = pxfc->pxfcPrev;
293 }
294
295 pxfc->pxfcNext = NULL;
296 pxfc->pxfcPrev = NULL;
297
298 // Now, add this node to the end of the matched node's additional changes list
299 if (!pxfcCheck->pxfcAdditionalChanges)
300 {
301 pxfcCheck->pxfcAdditionalChanges = pxfc;
302 pxfcCheck->cAdditionalChanges = 1;
303 }
304 else
305 {
306 pxfcLast = pxfcCheck->pxfcAdditionalChanges;
307 cAdditionalChanges = 1;
308 while (pxfcLast->pxfcNext)
309 {
310 pxfcLast = pxfcLast->pxfcNext;
311 ++cAdditionalChanges;
312 }
313 pxfcLast->pxfcNext = pxfc;
314 pxfc->pxfcPrev = pxfcLast;
315 pxfcCheck->cAdditionalChanges = ++cAdditionalChanges;
316 }
317 }
318 else
319 {
320 hr = E_NOTFOUND;
321 ExitOnRootFailure(hr, "failed to find matching ElementId: %ls", pxfc->pwzElementId);
322 }
323 }
324
325 pxfcCheck = pxfcCheck->pxfcNext;
326 }
327
328 pxfc = pxfcNext;
329 }
330
331LExit:
332
333 return hr;
334}
335
336
337static HRESULT BeginChangeFile(
338 __in LPCWSTR pwzFile,
339 __in int iCompAttributes,
340 __inout LPWSTR* ppwzCustomActionData
341 )
342{
343 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
344
345 HRESULT hr = S_OK;
346 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
347
348 LPBYTE pbData = NULL;
349 SIZE_T cbData = 0;
350
351 LPWSTR pwzRollbackCustomActionData = NULL;
352
353 if (fIs64Bit)
354 {
355 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
356 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
357 }
358 else
359 {
360 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
361 ExitOnFailure(hr, "failed to write file indicator to custom action data");
362 }
363
364 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
365 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
366
367 // If the file already exits, then we have to put it back the way it was on failure
368 if (FileExistsEx(pwzFile, NULL))
369 {
370 hr = FileRead(&pbData, &cbData, pwzFile);
371 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
372
373 // Set up the rollback for this file
374 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
375 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
376
377 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
378 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
379
380 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
381 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
382
383 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlConfigRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
384 ExitOnFailure(hr, "failed to schedule ExecXmlConfigRollback for file: %ls", pwzFile);
385
386 ReleaseStr(pwzRollbackCustomActionData);
387 }
388LExit:
389 ReleaseMem(pbData);
390
391 return hr;
392}
393
394
395static HRESULT WriteChangeData(
396 __in XML_CONFIG_CHANGE* pxfc,
397 __in eXmlAction action,
398 __inout LPWSTR* ppwzCustomActionData
399 )
400{
401 Assert(pxfc && ppwzCustomActionData);
402
403 HRESULT hr = S_OK;
404 XML_CONFIG_CHANGE* pxfcAdditionalChanges = NULL;
405 LPCWSTR wzElementPath = pxfc->pwzElementId ? pxfc->pwzElementId : pxfc->pwzElementPath;
406
407 hr = WcaWriteStringToCaData(wzElementPath, ppwzCustomActionData);
408 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", wzElementPath);
409
410 hr = WcaWriteStringToCaData(pxfc->pwzVerifyPath, ppwzCustomActionData);
411 ExitOnFailure(hr, "failed to write VerifyPath to custom action data: %ls", pxfc->pwzVerifyPath);
412
413 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
414 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
415
416 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
417 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
418
419 if (pxfc->iXmlFlags & XMLCONFIG_CREATE && pxfc->iXmlFlags & XMLCONFIG_ELEMENT && xaCreateElement == action && pxfc->pxfcAdditionalChanges)
420 {
421 hr = WcaWriteIntegerToCaData(pxfc->cAdditionalChanges, ppwzCustomActionData);
422 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
423
424 pxfcAdditionalChanges = pxfc->pxfcAdditionalChanges;
425 while (pxfcAdditionalChanges)
426 {
427 Assert((0 == lstrcmpW(pxfcAdditionalChanges->wzComponent, pxfc->wzComponent)) && 0 == pxfcAdditionalChanges->iXmlFlags && (0 == lstrcmpW(pxfcAdditionalChanges->wzFile, pxfc->wzFile)));
428
429 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->wzName, ppwzCustomActionData);
430 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
431
432 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->pwzValue, ppwzCustomActionData);
433 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
434
435 pxfcAdditionalChanges = pxfcAdditionalChanges->pxfcNext;
436 }
437 }
438 else
439 {
440 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
441 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
442 }
443
444LExit:
445 return hr;
446}
447
448
449/******************************************************************
450 SchedXmlConfig - entry point for XmlConfig Custom Action
451
452********************************************************************/
453extern "C" UINT __stdcall SchedXmlConfig(
454 __in MSIHANDLE hInstall
455 )
456{
457// AssertSz(FALSE, "debug SchedXmlConfig");
458
459 HRESULT hr = S_OK;
460 UINT er = ERROR_SUCCESS;
461
462 LPWSTR pwzCurrentFile = NULL;
463 BOOL fCurrentFileChanged = FALSE;
464
465 PMSIHANDLE hView = NULL;
466 PMSIHANDLE hRec = NULL;
467
468 XML_CONFIG_CHANGE* pxfcHead = NULL;
469 XML_CONFIG_CHANGE* pxfcTail = NULL; // TODO: do we need this any more?
470 XML_CONFIG_CHANGE* pxfc = NULL;
471
472 eXmlAction xa = xaUnknown;
473 eXmlPreserveDate xd;
474
475 LPWSTR pwzCustomActionData = NULL;
476
477 DWORD cFiles = 0;
478
479 // initialize
480 hr = WcaInitialize(hInstall, "SchedXmlConfig");
481 ExitOnFailure(hr, "failed to initialize");
482
483 hr = ReadXmlConfigTable(&pxfcHead, &pxfcTail);
484 MessageExitOnFailure(hr, msierrXmlConfigFailedRead, "failed to read Wix4XmlConfig table");
485
486 hr = ProcessChanges(&pxfcHead);
487 ExitOnFailure(hr, "failed to process Wix4XmlConfig changes");
488
489 // loop through all the xml configurations
490 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
491 {
492 // If this is a different file, or the first file...
493 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile))
494 {
495 // Remember the file we're currently working on
496 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
497 ExitOnFailure(hr, "failed to copy file name");
498
499 fCurrentFileChanged = TRUE;
500 }
501
502 //
503 // Figure out what action to take
504 //
505 xa = xaUnknown;
506
507 // If it's being installed or reinstalled or uninstalled and that matches
508 // what we are doing then calculate the right action.
509 if ((XMLCONFIG_INSTALL & pxfc->iXmlFlags && (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction) || WcaIsReInstalling(pxfc->isInstalled, pxfc->isAction))) ||
510 (XMLCONFIG_UNINSTALL & pxfc->iXmlFlags && WcaIsUninstalling(pxfc->isInstalled, pxfc->isAction)))
511 {
512 if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
513 {
514 xa = xaCreateElement;
515 }
516 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
517 {
518 xa = xaDeleteElement;
519 }
520 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
521 {
522 xa = xaDeleteValue;
523 }
524 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
525 {
526 xa = xaWriteValue;
527 }
528 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
529 {
530 xa = xaWriteDocument;
531 }
532 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
533 {
534 hr = E_INVALIDARG;
535 ExitOnFailure(hr, "Invalid flag configuration. Cannot delete a fragment node.");
536 }
537 }
538
539 if (XMLCONFIG_PRESERVE_MODIFIED & pxfc->iXmlFlags)
540 {
541 xd = xdPreserve;
542 }
543 else
544 {
545 xd= xdDontPreserve;
546 }
547
548 if (xaUnknown != xa)
549 {
550 if (fCurrentFileChanged)
551 {
552 hr = BeginChangeFile(pwzCurrentFile, pxfc->iCompAttributes, &pwzCustomActionData);
553 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
554
555 fCurrentFileChanged = FALSE;
556 ++cFiles;
557 }
558
559 hr = WcaWriteIntegerToCaData((int)xa, &pwzCustomActionData);
560 ExitOnFailure(hr, "failed to write action indicator custom action data");
561
562 hr = WcaWriteIntegerToCaData((int)xd, &pwzCustomActionData);
563 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
564
565 hr = WriteChangeData(pxfc, xa, &pwzCustomActionData);
566 ExitOnFailure(hr, "failed to write change data");
567 }
568 }
569
570 // If we looped through all records all is well
571 if (E_NOMOREITEMS == hr)
572 {
573 hr = S_OK;
574 }
575 ExitOnFailure(hr, "failed while looping through all objects to secure");
576
577 // Schedule the custom action and add to progress bar
578 if (pwzCustomActionData && *pwzCustomActionData)
579 {
580 Assert(0 < cFiles);
581
582 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlConfig"), pwzCustomActionData, cFiles * COST_XMLFILE);
583 ExitOnFailure(hr, "failed to schedule ExecXmlConfig action");
584 }
585
586LExit:
587 ReleaseStr(pwzCurrentFile);
588 ReleaseStr(pwzCustomActionData);
589
590 FreeXmlConfigChangeList(pxfcHead);
591
592 if (FAILED(hr))
593 {
594 er = ERROR_INSTALL_FAILURE;
595 }
596 return WcaFinalize(er);
597}
598
599
600/******************************************************************
601 ExecXmlConfig - entry point for XmlConfig Custom Action
602
603*******************************************************************/
604extern "C" UINT __stdcall ExecXmlConfig(
605 __in MSIHANDLE hInstall
606 )
607{
608 //AssertSz(FALSE, "debug ExecXmlConfig");
609 HRESULT hr = S_OK;
610 HRESULT hrOpenFailure = S_OK;
611 UINT er = ERROR_SUCCESS;
612
613#ifndef _WIN64
614 BOOL fIsFSRedirectDisabled = FALSE;
615#endif
616 BOOL fPreserveDate = FALSE;
617
618 LPWSTR pwzCustomActionData = NULL;
619 LPWSTR pwzData = NULL;
620 LPWSTR pwzFile = NULL;
621 LPWSTR pwzElementPath = NULL;
622 LPWSTR pwzVerifyPath = NULL;
623 LPWSTR pwzName = NULL;
624 LPWSTR pwzValue = NULL;
625 LPWSTR pwz = NULL;
626 int cAdditionalChanges = 0;
627
628 IXMLDOMDocument* pixd = NULL;
629 IXMLDOMNode* pixn = NULL;
630 IXMLDOMNode* pixnVerify = NULL;
631 IXMLDOMNode* pixnNewNode = NULL;
632 IXMLDOMNode* pixnRemovedChild = NULL;
633
634 IXMLDOMDocument* pixdNew = NULL;
635 IXMLDOMElement* pixeNew = NULL;
636
637 FILETIME ft;
638
639 int id = IDRETRY;
640
641 eXmlAction xa;
642 eXmlPreserveDate xd;
643
644 // initialize
645 hr = WcaInitialize(hInstall, "ExecXmlConfig");
646 ExitOnFailure(hr, "failed to initialize");
647
648 hr = XmlInitialize();
649 ExitOnFailure(hr, "failed to initialize xml utilities");
650
651 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
652 ExitOnFailure(hr, "failed to get CustomActionData");
653
654 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
655
656 pwz = pwzCustomActionData;
657
658 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
659 ExitOnFailure(hr, "failed to process CustomActionData");
660
661#ifndef _WIN64
662 // Initialize the Wow64 API - store the result in fWow64APIPresent
663 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
664 WcaInitializeWow64();
665 BOOL fIsWow64Process = WcaIsWow64Process();
666#endif
667
668 if (xaOpenFile != xa && xaOpenFilex64 != xa)
669 {
670 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
671 }
672
673 // loop through all the passed in data
674 while (pwz && *pwz)
675 {
676 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
677 ExitOnFailure(hr, "failed to read file name from custom action data");
678
679 // Default to not preserve date, preserve it if any modifications require us to
680 fPreserveDate = FALSE;
681
682 // Open the file
683 ReleaseNullObject(pixd);
684
685#ifndef _WIN64
686 if (xaOpenFilex64 == xa)
687 {
688 if (!fIsWow64Process)
689 {
690 hr = E_NOTIMPL;
691 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
692 }
693
694 hr = WcaDisableWow64FSRedirection();
695 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
696
697 fIsFSRedirectDisabled = TRUE;
698 }
699#endif
700
701 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
702 if (FAILED(hr))
703 {
704 // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter.
705 hrOpenFailure = hr;
706 hr = S_OK;
707 }
708 else
709 {
710 hrOpenFailure = S_OK;
711 }
712
713 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
714
715 while (pwz && *pwz)
716 {
717 // If we skip past an element that has additional changes we need to strip them off the stream before
718 // moving on to the next element. Do that now and then restart the outer loop.
719 if (cAdditionalChanges > 0)
720 {
721 while (cAdditionalChanges > 0)
722 {
723 hr = WcaReadStringFromCaData(&pwz, &pwzName);
724 ExitOnFailure(hr, "failed to process CustomActionData");
725 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
726 ExitOnFailure(hr, "failed to process CustomActionData");
727
728 cAdditionalChanges--;
729 }
730 continue;
731 }
732
733 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
734 ExitOnFailure(hr, "failed to process CustomActionData");
735
736 // Break if we need to move on to a different file
737 if (xaOpenFile == xa || xaOpenFilex64 == xa)
738 {
739 break;
740 }
741
742 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
743 ExitOnFailure(hr, "failed to process CustomActionData");
744
745 if (xdPreserve == xd)
746 {
747 fPreserveDate = TRUE;
748 }
749
750 // Get path, name, and value to be written
751 hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
752 ExitOnFailure(hr, "failed to process CustomActionData");
753 hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
754 ExitOnFailure(hr, "failed to process CustomActionData");
755 hr = WcaReadStringFromCaData(&pwz, &pwzName);
756 ExitOnFailure(hr, "failed to process CustomActionData");
757 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
758 ExitOnFailure(hr, "failed to process CustomActionData");
759 hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
760 ExitOnFailure(hr, "failed to process CustomActionData");
761
762 // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone.
763 if (FAILED(hrOpenFailure))
764 {
765 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
766 {
767 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
768 }
769 else
770 {
771 continue;
772 }
773 }
774
775 // Select the node we're about to modify
776 ReleaseNullObject(pixn);
777
778 hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);
779
780 // If we failed to find the node that we are going to add to, we've got a problem. Otherwise, just continue since the node's already gone.
781 if (S_FALSE == hr)
782 {
783 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
784 {
785 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
786 }
787 else
788 {
789 hr = S_OK;
790 continue;
791 }
792 }
793
794 MessageExitOnFailure(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);
795
796 // Make the modification
797 switch (xa)
798 {
799 case xaWriteValue:
800 if (pwzName && *pwzName)
801 {
802 // We're setting an attribute
803 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
804 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
805 }
806 else
807 {
808 // We're setting the text of the node
809 hr = XmlSetText(pixn, pwzValue);
810 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzElementPath);
811 }
812 break;
813 case xaWriteDocument:
814 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
815 {
816 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
817 if (S_OK == hr)
818 {
819 // We found the verify path which means we have no further work to do
820 continue;
821 }
822 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
823 }
824
825 hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
826 ExitOnFailure(hr, "Failed to load value as document.");
827
828 hr = pixdNew->get_documentElement(&pixeNew);
829 ExitOnFailure(hr, "Failed to get document element.");
830
831 hr = pixn->appendChild(pixeNew, NULL);
832 ExitOnFailure(hr, "Failed to append document element on to parent element.");
833
834 ReleaseNullObject(pixeNew);
835 ReleaseNullObject(pixdNew);
836 break;
837
838 case xaCreateElement:
839 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
840 {
841 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
842 if (S_OK == hr)
843 {
844 // We found the verify path which means we have no further work to do
845 continue;
846 }
847 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
848 }
849
850 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
851 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
852
853 if (pwzValue && *pwzValue)
854 {
855 hr = XmlSetText(pixnNewNode, pwzValue);
856 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
857 }
858
859 while (cAdditionalChanges > 0)
860 {
861 hr = WcaReadStringFromCaData(&pwz, &pwzName);
862 ExitOnFailure(hr, "failed to process CustomActionData");
863 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
864 ExitOnFailure(hr, "failed to process CustomActionData");
865
866 // Set the additional attribute
867 hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
868 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
869
870 cAdditionalChanges--;
871 }
872
873 ReleaseNullObject(pixnNewNode);
874 break;
875 case xaDeleteValue:
876 if (pwzName && *pwzName)
877 {
878 // Delete the attribute
879 hr = XmlRemoveAttribute(pixn, pwzName);
880 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
881 }
882 else
883 {
884 // Clear the text value for the node
885 hr = XmlSetText(pixn, L"");
886 ExitOnFailure(hr, "failed to clear text value");
887 }
888 break;
889 case xaDeleteElement:
890 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
891 {
892 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
893 if (S_OK == hr)
894 {
895 hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
896 ExitOnFailure(hr, "failed to remove created child element");
897
898 ReleaseNullObject(pixnRemovedChild);
899 }
900 else
901 {
902 WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting. Skipping...", pwzVerifyPath);
903 hr = S_OK;
904 }
905 }
906 else
907 {
908 // TODO: This requires a VerifyPath to delete an element. Should we support not having one?
909 WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
910 }
911 break;
912 default:
913 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
914 break;
915 }
916 }
917
918
919 // Now that we've made all of the changes to this file, save it and move on to the next
920 if (S_OK == hrOpenFailure)
921 {
922 if (fPreserveDate)
923 {
924 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
925 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
926 }
927
928 int iSaveAttempt = 0;
929
930 do
931 {
932 hr = XmlSaveDocument(pixd, pwzFile);
933 if (FAILED(hr))
934 {
935 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
936 switch (id)
937 {
938 case IDABORT:
939 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
940 case IDRETRY:
941 hr = S_FALSE; // hit me, baby, one more time
942 break;
943 case IDIGNORE:
944 hr = S_OK; // pretend everything is okay and bail
945 break;
946 case 0: // No UI case, MsiProcessMessage returns 0
947 if (STIERR_SHARING_VIOLATION == hr)
948 {
949 // Only in case of sharing violation do we retry 30 times, once a second.
950 if (iSaveAttempt < 30)
951 {
952 hr = S_FALSE;
953 ++iSaveAttempt;
954 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
955 Sleep(1000);
956 }
957 else
958 {
959 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
960 }
961 }
962 break;
963 default: // Unknown error
964 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
965 }
966 }
967 } while (S_FALSE == hr);
968
969 if (fPreserveDate)
970 {
971 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
972 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
973 }
974
975#ifndef _WIN64
976 if (fIsFSRedirectDisabled)
977 {
978 fIsFSRedirectDisabled = FALSE;
979 WcaRevertWow64FSRedirection();
980 }
981#endif
982 }
983 }
984
985LExit:
986#ifndef _WIN64
987 // Make sure we revert FS Redirection if necessary before exiting
988 if (fIsFSRedirectDisabled)
989 {
990 fIsFSRedirectDisabled = FALSE;
991 WcaRevertWow64FSRedirection();
992 }
993 WcaFinalizeWow64();
994#endif
995
996 ReleaseStr(pwzCustomActionData);
997 ReleaseStr(pwzData);
998 ReleaseStr(pwzFile);
999 ReleaseStr(pwzElementPath);
1000 ReleaseStr(pwzVerifyPath);
1001 ReleaseStr(pwzName);
1002 ReleaseStr(pwzValue);
1003
1004 ReleaseObject(pixeNew);
1005 ReleaseObject(pixdNew);
1006
1007 ReleaseObject(pixn);
1008 ReleaseObject(pixd);
1009 ReleaseObject(pixnNewNode);
1010 ReleaseObject(pixnRemovedChild);
1011
1012 XmlUninitialize();
1013
1014 if (FAILED(hr))
1015 {
1016 er = ERROR_INSTALL_FAILURE;
1017 }
1018 return WcaFinalize(er);
1019}
1020
1021
1022/******************************************************************
1023 ExecXmlConfigRollback - entry point for XmlConfig rollback Custom Action
1024
1025*******************************************************************/
1026extern "C" UINT __stdcall ExecXmlConfigRollback(
1027 __in MSIHANDLE hInstall
1028 )
1029{
1030// AssertSz(FALSE, "debug ExecXmlConfigRollback");
1031 HRESULT hr = S_OK;
1032 UINT er = ERROR_SUCCESS;
1033
1034 int iIs64Bit;
1035#ifndef _WIN64
1036 BOOL fIs64Bit = FALSE;
1037#endif
1038
1039 LPWSTR pwzCustomActionData = NULL;
1040 LPWSTR pwz = NULL;
1041 LPWSTR pwzFileName = NULL;
1042 LPBYTE pbData = NULL;
1043 DWORD_PTR cbData = 0;
1044
1045 FILETIME ft;
1046
1047 HANDLE hFile = INVALID_HANDLE_VALUE;
1048
1049 // initialize
1050 hr = WcaInitialize(hInstall, "ExecXmlConfigRollback");
1051 ExitOnFailure(hr, "failed to initialize");
1052
1053
1054 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
1055 ExitOnFailure(hr, "failed to get CustomActionData");
1056
1057 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
1058
1059 pwz = pwzCustomActionData;
1060
1061 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
1062 ExitOnFailure(hr, "failed to read component bitness from custom action data");
1063
1064 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
1065 ExitOnFailure(hr, "failed to read file name from custom action data");
1066
1067 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
1068 ExitOnFailure(hr, "failed to read file contents from custom action data");
1069
1070#ifndef _WIN64
1071 fIs64Bit = (BOOL)iIs64Bit;
1072
1073 if (fIs64Bit)
1074 {
1075 hr = WcaInitializeWow64();
1076 if (S_FALSE == hr)
1077 {
1078 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
1079 }
1080 ExitOnFailure(hr, "failed to initialize Wow64 API");
1081
1082 if (!WcaIsWow64Process())
1083 {
1084 hr = E_NOTIMPL;
1085 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the Wow64 API is unavailable.");
1086 }
1087
1088 hr = WcaDisableWow64FSRedirection();
1089 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
1090 }
1091#endif
1092
1093 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
1094 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
1095
1096 // Open the file
1097 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
1098 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
1099
1100 // Write out the old data
1101 hr = FileWriteHandle(hFile, pbData, cbData);
1102 ExitOnFailure(hr, "failed to write to file: %ls", pwzFileName);
1103
1104 ReleaseFile(hFile);
1105
1106 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
1107 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
1108
1109LExit:
1110 ReleaseStr(pwzCustomActionData);
1111 ReleaseStr(pwzFileName);
1112
1113 ReleaseFile(hFile);
1114
1115#ifndef _WIN64
1116 if (fIs64Bit)
1117 {
1118 WcaRevertWow64FSRedirection();
1119 WcaFinalizeWow64();
1120 }
1121#endif
1122
1123 ReleaseMem(pbData);
1124
1125 if (FAILED(hr))
1126 {
1127 er = ERROR_INSTALL_FAILURE;
1128 }
1129 return WcaFinalize(er);
1130}
diff --git a/src/ext/Util/ca/XmlFile.cpp b/src/ext/Util/ca/XmlFile.cpp
new file mode 100644
index 00000000..04a4ae98
--- /dev/null
+++ b/src/ext/Util/ca/XmlFile.cpp
@@ -0,0 +1,940 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define XMLFILE_CREATE_ELEMENT 0x00000001
6#define XMLFILE_DELETE_VALUE 0x00000002
7#define XMLFILE_BULKWRITE_VALUE 0x00000004
8
9#define XMLFILE_DONT_UNINSTALL 0x00010000
10#define XMLFILE_PRESERVE_MODIFIED 0x00001000
11#define XMLFILE_USE_XPATH 0x00000100
12
13extern BOOL vfMsxml30;
14
15enum eXmlAction
16{
17 xaOpenFile = 1,
18 xaOpenFilex64,
19 xaWriteValue,
20 xaDeleteValue,
21 xaCreateElement,
22 xaDeleteElement,
23 xaBulkWriteValue,
24};
25
26enum eXmlPreserveDate
27{
28 xdDontPreserve = 0,
29 xdPreserve
30};
31
32enum eXmlSelectionLanguage
33{
34 xsXSLPattern = 0,
35 xsXPath = 1,
36};
37
38LPCWSTR vcsXmlFileQuery =
39 L"SELECT `Wix4XmlFile`.`Wix4XmlFile`, `Wix4XmlFile`.`File`, `Wix4XmlFile`.`ElementPath`, `Wix4XmlFile`.`Name`, `Wix4XmlFile`.`Value`, "
40 L"`Wix4XmlFile`.`Flags`, `Wix4XmlFile`.`Component_`, `Component`.`Attributes` "
41 L"FROM `Wix4XmlFile`,`Component` WHERE `Wix4XmlFile`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
42enum eXmlFileQuery { xfqXmlFile = 1, xfqFile, xfqXPath, xfqName, xfqValue, xfqXmlFlags, xfqComponent, xfqCompAttributes };
43
44struct XML_FILE_CHANGE
45{
46 WCHAR wzId[MAX_DARWIN_KEY];
47
48 INSTALLSTATE isInstalled;
49 INSTALLSTATE isAction;
50
51 WCHAR wzFile[MAX_PATH];
52 LPWSTR pwzElementPath;
53 WCHAR wzName[MAX_DARWIN_COLUMN];
54 LPWSTR pwzValue;
55
56 int iXmlFlags;
57 int iCompAttributes;
58
59 XML_FILE_CHANGE* pxfcPrev;
60 XML_FILE_CHANGE* pxfcNext;
61};
62
63//static HRESULT FreeXmlFileChangeList(
64// __in XML_FILE_CHANGE* pxfcList
65// )
66//{
67// HRESULT hr = S_OK;
68//
69// XML_FILE_CHANGE* pxfcDelete;
70// while(pxfcList)
71// {
72// pxfcDelete = pxfcList;
73// pxfcList = pxfcList->pxfcNext;
74//
75// ReleaseStr(pxfcDelete->pwzElementPath);
76// ReleaseStr(pxfcDelete->pwzValue);
77//
78// hr = MemFree(pxfcDelete);
79// ExitOnFailure(hr, "failed to free xml file change list item");
80// }
81//
82//LExit:
83// return hr;
84//}
85
86static HRESULT AddXmlFileChangeToList(
87 __inout XML_FILE_CHANGE** ppxfcHead,
88 __inout XML_FILE_CHANGE** ppxfcTail
89 )
90{
91 Assert(ppxfcHead && ppxfcTail);
92
93 HRESULT hr = S_OK;
94
95 XML_FILE_CHANGE* pxfc = static_cast<XML_FILE_CHANGE*>(MemAlloc(sizeof(XML_FILE_CHANGE), TRUE));
96 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
97
98 // Add it to the end of the list
99 if (NULL == *ppxfcHead)
100 {
101 *ppxfcHead = pxfc;
102 *ppxfcTail = pxfc;
103 }
104 else
105 {
106 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
107 (*ppxfcTail)->pxfcNext = pxfc;
108 pxfc->pxfcPrev = *ppxfcTail;
109 *ppxfcTail = pxfc;
110 }
111
112LExit:
113 return hr;
114}
115
116
117static HRESULT ReadXmlFileTable(
118 __inout XML_FILE_CHANGE** ppxfcHead,
119 __inout XML_FILE_CHANGE** ppxfcTail
120 )
121{
122 Assert(ppxfcHead && ppxfcTail);
123
124 HRESULT hr = S_OK;
125 UINT er = ERROR_SUCCESS;
126
127 PMSIHANDLE hView = NULL;
128 PMSIHANDLE hRec = NULL;
129
130 LPWSTR pwzData = NULL;
131
132 // check to see if necessary tables are specified
133 if (S_FALSE == WcaTableExists(L"Wix4XmlFile"))
134 {
135 ExitFunction1(hr = S_FALSE);
136 }
137
138 // loop through all the xml configurations
139 hr = WcaOpenExecuteView(vcsXmlFileQuery, &hView);
140 ExitOnFailure(hr, "failed to open view on Wix4XmlFile table");
141
142 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
143 {
144 hr = AddXmlFileChangeToList(ppxfcHead, ppxfcTail);
145 ExitOnFailure(hr, "failed to add xml file change to list");
146
147 // Get record Id
148 hr = WcaGetRecordString(hRec, xfqXmlFile, &pwzData);
149 ExitOnFailure(hr, "failed to get Wix4XmlFile record Id");
150 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
151 ExitOnFailure(hr, "failed to copy Wix4XmlFile record Id");
152
153 // Get component name
154 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
155 ExitOnFailure(hr, "failed to get component name for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
156
157 // Get the component's state
158 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
159 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData);
160
161 // Get the xml file
162 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
163 ExitOnFailure(hr, "failed to get xml file for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
164 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
165 ExitOnFailure(hr, "failed to copy xml file path");
166
167 // Get the Wix4XmlFile table flags
168 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
169 ExitOnFailure(hr, "failed to get Wix4XmlFile flags for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
170
171 // Get the XPath
172 hr = WcaGetRecordFormattedString(hRec, xfqXPath, &(*ppxfcTail)->pwzElementPath);
173 ExitOnFailure(hr, "failed to get XPath for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
174
175 // Get the name
176 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
177 ExitOnFailure(hr, "failed to get Name for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
178 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
179 ExitOnFailure(hr, "failed to copy name of element");
180
181 // Get the value
182 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
183 ExitOnFailure(hr, "failed to get Value for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
184 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
185 ExitOnFailure(hr, "failed to allocate buffer for value");
186
187 // Get the component attributes
188 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
189 ExitOnFailure(hr, "failed to get component attributes for Wix4XmlFile: %ls", (*ppxfcTail)->wzId);
190 }
191
192 // if we looped through all records all is well
193 if (E_NOMOREITEMS == hr)
194 hr = S_OK;
195 ExitOnFailure(hr, "failed while looping through all objects to secure");
196
197LExit:
198 ReleaseStr(pwzData);
199
200 return hr;
201}
202
203
204static HRESULT BeginChangeFile(
205 __in LPCWSTR pwzFile,
206 __in XML_FILE_CHANGE* pxfc,
207 __inout LPWSTR* ppwzCustomActionData
208 )
209{
210 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
211
212 HRESULT hr = S_OK;
213 BOOL fIs64Bit = pxfc->iCompAttributes & msidbComponentAttributes64bit;
214 BOOL fUseXPath = pxfc->iXmlFlags & XMLFILE_USE_XPATH;
215 LPBYTE pbData = NULL;
216 SIZE_T cbData = 0;
217
218 LPWSTR pwzRollbackCustomActionData = NULL;
219
220 if (fIs64Bit)
221 {
222 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
223 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
224 }
225 else
226 {
227 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
228 ExitOnFailure(hr, "failed to write file indicator to custom action data");
229 }
230 if (fUseXPath)
231 {
232 hr = WcaWriteIntegerToCaData((int)xsXPath, ppwzCustomActionData);
233 ExitOnFailure(hr, "failed to write XPath selectionlanguage indicator to custom action data");
234 }
235 else
236 {
237 hr = WcaWriteIntegerToCaData((int)xsXSLPattern, ppwzCustomActionData);
238 ExitOnFailure(hr, "failed to write XSLPattern selectionlanguage indicator to custom action data");
239 }
240 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
241 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
242
243 // If the file already exits, then we have to put it back the way it was on failure
244 if (FileExistsEx(pwzFile, NULL))
245 {
246 hr = FileRead(&pbData, &cbData, pwzFile);
247 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
248
249 // Set up the rollback for this file
250 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
251 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
252
253 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
254 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
255
256 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
257 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
258
259 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlFileRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
260 ExitOnFailure(hr, "failed to schedule ExecXmlFileRollback for file: %ls", pwzFile);
261
262 ReleaseStr(pwzRollbackCustomActionData);
263 }
264LExit:
265 ReleaseMem(pbData);
266
267 return hr;
268}
269
270
271static HRESULT WriteChangeData(
272 __in XML_FILE_CHANGE* pxfc,
273 __inout LPWSTR* ppwzCustomActionData
274 )
275{
276 Assert(pxfc && ppwzCustomActionData);
277
278 HRESULT hr = S_OK;
279
280 hr = WcaWriteStringToCaData(pxfc->pwzElementPath, ppwzCustomActionData);
281 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", pxfc->pwzElementPath);
282
283 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
284 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
285
286 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
287 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
288
289LExit:
290 return hr;
291}
292
293
294/******************************************************************
295 SchedXmlFile - entry point for XmlFile Custom Action
296
297********************************************************************/
298extern "C" UINT __stdcall SchedXmlFile(
299 __in MSIHANDLE hInstall
300 )
301{
302// AssertSz(FALSE, "debug SchedXmlFile");
303
304 HRESULT hr = S_OK;
305 UINT er = ERROR_SUCCESS;
306
307 LPWSTR pwzCurrentFile = NULL;
308 BOOL fCurrentFileChanged = FALSE;
309 BOOL fCurrentUseXPath = FALSE;
310
311 PMSIHANDLE hView = NULL;
312 PMSIHANDLE hRec = NULL;
313
314 XML_FILE_CHANGE* pxfcHead = NULL;
315 XML_FILE_CHANGE* pxfcTail = NULL;
316 XML_FILE_CHANGE* pxfc = NULL;
317 XML_FILE_CHANGE* pxfcUninstall = NULL;
318
319 LPWSTR pwzCustomActionData = NULL;
320
321 DWORD cFiles = 0;
322
323 // initialize
324 hr = WcaInitialize(hInstall, "SchedXmlFile");
325 ExitOnFailure(hr, "failed to initialize");
326
327 hr = ReadXmlFileTable(&pxfcHead, &pxfcTail);
328 if (S_FALSE == hr)
329 {
330 WcaLog(LOGMSG_VERBOSE, "Skipping SchedXmlFile because Wix4XmlFile table not present");
331 ExitFunction1(hr = S_OK);
332 }
333
334 MessageExitOnFailure(hr, msierrXmlFileFailedRead, "failed to read Wix4XmlFile table");
335
336 // loop through all the xml configurations
337 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
338 {
339 // If this is the first file, a different file, the last file, or the SelectionLanguage property changes...
340 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile) || NULL == pxfc->pxfcNext || fCurrentUseXPath != ((XMLFILE_USE_XPATH & pxfc->iXmlFlags)))
341 {
342 // If this isn't the first file
343 if (NULL != pwzCurrentFile)
344 {
345 // Do the uninstall work for the current file by walking backwards through the list (so the sequence is reversed)
346 for (pxfcUninstall = ((NULL != pxfc->pxfcNext) ? pxfc->pxfcPrev : pxfc); pxfcUninstall && 0 == lstrcmpW(pwzCurrentFile, pxfcUninstall->wzFile) && fCurrentUseXPath == ((XMLFILE_USE_XPATH & pxfcUninstall->iXmlFlags)); pxfcUninstall = pxfcUninstall->pxfcPrev)
347 {
348 // If it's being uninstalled
349 if (WcaIsUninstalling(pxfcUninstall->isInstalled, pxfcUninstall->isAction))
350 {
351 // Uninstall the change
352 if (!(XMLFILE_DONT_UNINSTALL & pxfcUninstall->iXmlFlags))
353 {
354 if (!fCurrentFileChanged)
355 {
356 hr = BeginChangeFile(pwzCurrentFile, pxfcUninstall, &pwzCustomActionData);
357 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
358
359 fCurrentFileChanged = TRUE;
360 ++cFiles;
361 }
362 if (XMLFILE_CREATE_ELEMENT & pxfcUninstall->iXmlFlags)
363 {
364 hr = WcaWriteIntegerToCaData((int)xaDeleteElement, &pwzCustomActionData);
365 ExitOnFailure(hr, "failed to write delete element action indicator to custom action data");
366 }
367 else
368 {
369 hr = WcaWriteIntegerToCaData((int)xaDeleteValue, &pwzCustomActionData);
370 ExitOnFailure(hr, "failed to write delete value action indicator to custom action data");
371 }
372
373 if (XMLFILE_PRESERVE_MODIFIED & pxfc->iXmlFlags)
374 {
375 hr = WcaWriteIntegerToCaData((int)xdPreserve, &pwzCustomActionData);
376 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
377 }
378 else
379 {
380 hr = WcaWriteIntegerToCaData((int)xdDontPreserve, &pwzCustomActionData);
381 ExitOnFailure(hr, "failed to write Don't Preserve Date indicator to custom action data");
382 }
383
384 hr = WriteChangeData(pxfcUninstall, &pwzCustomActionData);
385 ExitOnFailure(hr, "failed to write uninstall change data");
386 }
387 }
388 }
389 }
390
391 // Remember the file we're currently working on
392 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
393 ExitOnFailure(hr, "failed to copy file name");
394 fCurrentUseXPath = (XMLFILE_USE_XPATH & pxfc->iXmlFlags);
395
396 // We haven't changed the current file yet
397 fCurrentFileChanged = FALSE;
398 }
399
400 // If it's being installed
401 if (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction))
402 {
403 if (!fCurrentFileChanged)
404 {
405 hr = BeginChangeFile(pwzCurrentFile, pxfc, &pwzCustomActionData);
406 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
407 fCurrentFileChanged = TRUE;
408 ++cFiles;
409 }
410
411 // Install the change
412 if (XMLFILE_CREATE_ELEMENT & pxfc->iXmlFlags)
413 {
414 hr = WcaWriteIntegerToCaData((int)xaCreateElement, &pwzCustomActionData);
415 ExitOnFailure(hr, "failed to write create element action indicator to custom action data");
416 }
417 else if (XMLFILE_DELETE_VALUE & pxfc->iXmlFlags)
418 {
419 hr = WcaWriteIntegerToCaData((int)xaDeleteValue, &pwzCustomActionData);
420 ExitOnFailure(hr, "failed to write delete value action indicator to custom action data");
421 }
422 else if (XMLFILE_BULKWRITE_VALUE & pxfc->iXmlFlags)
423 {
424 hr = WcaWriteIntegerToCaData((int)xaBulkWriteValue, &pwzCustomActionData);
425 ExitOnFailure(hr, "failed to write builkwrite value action indicator to custom action data");
426 }
427 else
428 {
429 hr = WcaWriteIntegerToCaData((int)xaWriteValue, &pwzCustomActionData);
430 ExitOnFailure(hr, "failed to write file indicator to custom action data");
431 }
432
433 if (XMLFILE_PRESERVE_MODIFIED & pxfc->iXmlFlags)
434 {
435 hr = WcaWriteIntegerToCaData((int)xdPreserve, &pwzCustomActionData);
436 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
437 }
438 else
439 {
440 hr = WcaWriteIntegerToCaData((int)xdDontPreserve, &pwzCustomActionData);
441 ExitOnFailure(hr, "failed to write Don't Preserve Date indicator to custom action data");
442 }
443
444 hr = WriteChangeData(pxfc, &pwzCustomActionData);
445 ExitOnFailure(hr, "failed to write change data");
446 }
447 }
448
449 // If we looped through all records all is well
450 if (E_NOMOREITEMS == hr)
451 hr = S_OK;
452 ExitOnFailure(hr, "failed while looping through all objects to secure");
453
454 // Schedule the custom action and add to progress bar
455 if (pwzCustomActionData && *pwzCustomActionData)
456 {
457 Assert(0 < cFiles);
458
459 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecXmlFile"), pwzCustomActionData, cFiles * COST_XMLFILE);
460 ExitOnFailure(hr, "failed to schedule ExecXmlFile action");
461 }
462
463LExit:
464 ReleaseStr(pwzCurrentFile);
465 ReleaseStr(pwzCustomActionData);
466
467 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
468}
469
470
471/******************************************************************
472 ExecXmlFile - entry point for XmlFile Custom Action
473
474*******************************************************************/
475extern "C" UINT __stdcall ExecXmlFile(
476 __in MSIHANDLE hInstall
477 )
478{
479// AssertSz(FALSE, "debug ExecXmlFile");
480 HRESULT hr = S_OK;
481 HRESULT hrOpenFailure = S_OK;
482 UINT er = ERROR_SUCCESS;
483
484 BOOL fIsFSRedirectDisabled = FALSE;
485 BOOL fPreserveDate = FALSE;
486
487 int id = IDRETRY;
488
489 LPWSTR pwzCustomActionData = NULL;
490 LPWSTR pwzData = NULL;
491 LPWSTR pwzFile = NULL;
492 LPWSTR pwzXPath = NULL;
493 LPWSTR pwzName = NULL;
494 LPWSTR pwzValue = NULL;
495 LPWSTR pwz = NULL;
496
497 IXMLDOMDocument* pixd = NULL;
498 IXMLDOMNode* pixn = NULL;
499 IXMLDOMNode* pixnNewNode = NULL;
500 IXMLDOMNodeList* pixNodes = NULL;
501 IXMLDOMDocument2 *pixdDocument2 = NULL;
502
503 FILETIME ft;
504
505 BSTR bstrProperty = ::SysAllocString(L"SelectionLanguage");
506 ExitOnNull(bstrProperty, hr, E_OUTOFMEMORY, "failed SysAllocString");
507 VARIANT varValue;
508 ::VariantInit(&varValue);
509 varValue.vt = VT_BSTR;
510 varValue.bstrVal = ::SysAllocString(L"XPath");
511 ExitOnNull(varValue.bstrVal, hr, E_OUTOFMEMORY, "failed SysAllocString");
512 eXmlAction xa;
513 eXmlPreserveDate xd;
514 eXmlSelectionLanguage xl;
515
516 // initialize
517 hr = WcaInitialize(hInstall, "ExecXmlFile");
518 ExitOnFailure(hr, "failed to initialize");
519
520 hr = XmlInitialize();
521 ExitOnFailure(hr, "failed to initialize xml utilities");
522
523 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
524 ExitOnFailure(hr, "failed to get CustomActionData");
525
526 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
527
528 pwz = pwzCustomActionData;
529
530 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
531 ExitOnFailure(hr, "failed to process CustomActionData");
532
533#ifndef _WIN64
534 // Initialize the Wow64 API - store the result in fWow64APIPresent
535 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
536 WcaInitializeWow64();
537 BOOL fIsWow64Process = WcaIsWow64Process();
538#endif
539
540 if (xaOpenFile != xa && xaOpenFilex64 != xa)
541 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
542
543 // loop through all the passed in data
544 while (pwz && *pwz)
545 {
546 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xl);
547 ExitOnFailure(hr, "failed to process CustomActionData");
548
549 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
550 ExitOnFailure(hr, "failed to read file name from custom action data");
551
552 // Default to not preserve the modified date
553 fPreserveDate = FALSE;
554
555 // Open the file
556 ReleaseNullObject(pixd);
557
558 if (xaOpenFilex64 == xa)
559 {
560#ifndef _WIN64
561 if (!fIsWow64Process)
562 {
563 hr = E_NOTIMPL;
564 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
565 }
566
567 hr = WcaDisableWow64FSRedirection();
568 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
569
570 fIsFSRedirectDisabled = TRUE;
571#endif
572 }
573
574 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
575 if (FAILED(hr))
576 {
577 // Ignore the return code for now. If they try to add something, we'll fail the install. If all they do is remove stuff then it doesn't matter.
578 hrOpenFailure = hr;
579 hr = S_OK;
580 }
581 else
582 {
583 hrOpenFailure = S_OK;
584 }
585 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
586
587 if (xsXPath == xl)
588 {
589 if (vfMsxml30)
590 {
591 // If we failed to open the file, don't fail immediately; just skip setting the selection language, and we'll fail later if appropriate
592 if (SUCCEEDED(hrOpenFailure))
593 {
594 hr = pixd->QueryInterface(XmlUtil_IID_IXMLDOMDocument2, (void**)&pixdDocument2);
595 ExitOnFailure(hr, "failed in querying IXMLDOMDocument2 interface");
596 hr = pixdDocument2->setProperty(bstrProperty, varValue);
597 ExitOnFailure(hr, "failed in setting SelectionLanguage");
598 }
599 }
600 else
601 {
602 ExitOnFailure(hr = E_NOTIMPL, "Error: current MSXML version does not support xpath query.");
603 }
604 }
605
606 while (pwz && *pwz)
607 {
608 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
609 ExitOnFailure(hr, "failed to process CustomActionData");
610
611 // Break if we need to move on to a different file
612 if (xaOpenFile == xa || xaOpenFilex64 == xa)
613 break;
614
615 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
616 ExitOnFailure(hr, "failed to process CustomActionData");
617
618 if (xdPreserve == xd)
619 {
620 fPreserveDate = TRUE;
621 }
622
623 // Get path, name, and value to be written
624 hr = WcaReadStringFromCaData(&pwz, &pwzXPath);
625 ExitOnFailure(hr, "failed to process CustomActionData");
626 hr = WcaReadStringFromCaData(&pwz, &pwzName);
627 ExitOnFailure(hr, "failed to process CustomActionData");
628 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
629 ExitOnFailure(hr, "failed to process CustomActionData");
630
631 // If we failed to open the file and we're adding something to the file, we've got a problem. Otherwise, just continue on since the file's already gone.
632 if (FAILED(hrOpenFailure))
633 {
634 if (xaCreateElement == xa || xaWriteValue == xa || xaBulkWriteValue == xa)
635 {
636 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlFileFailedOpen, "failed to load XML file: %ls", pwzFile);
637 }
638 else
639 {
640 continue;
641 }
642 }
643
644 // Select the node we're about to modify
645 ReleaseNullObject(pixn);
646
647 if (xaBulkWriteValue == xa)
648 {
649 hr = XmlSelectNodes(pixd, pwzXPath, &pixNodes);
650 if (S_FALSE == hr)
651 {
652 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
653 }
654
655 MessageExitOnFailure(hr, msierrXmlFileFailedSelect, "failed to find any nodes: %ls in XML file: %ls", pwzXPath, pwzFile);
656 for (;;)
657 {
658 pixNodes->nextNode(&pixn);
659 if (NULL == pixn)
660 break;
661
662 if (pwzName && *pwzName)
663 {
664 // We're setting an attribute
665 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
666 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
667 }
668 else
669 {
670 // We're setting the text of the node
671 hr = XmlSetText(pixn, pwzValue);
672 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzXPath);
673 }
674 ReleaseNullObject(pixn);
675 }
676 }
677 else
678 {
679 hr = XmlSelectSingleNode(pixd, pwzXPath, &pixn);
680 if (S_FALSE == hr)
681 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
682 MessageExitOnFailure(hr, msierrXmlFileFailedSelect, "failed to find node: %ls in XML file: %ls", pwzXPath, pwzFile);
683
684 // Make the modification
685 if (xaWriteValue == xa)
686 {
687 if (pwzName && *pwzName)
688 {
689 // We're setting an attribute
690 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
691 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
692 }
693 else
694 {
695 // We're setting the text of the node
696 hr = XmlSetText(pixn, pwzValue);
697 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzXPath);
698 }
699 }
700 else if (xaCreateElement == xa)
701 {
702 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
703 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
704
705 if (pwzValue && *pwzValue)
706 {
707 hr = XmlSetText(pixnNewNode, pwzValue);
708 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
709 }
710
711 ReleaseNullObject(pixnNewNode);
712 }
713 else if (xaDeleteValue == xa)
714 {
715 if (pwzName && *pwzName)
716 {
717 // Delete the attribute
718 hr = XmlRemoveAttribute(pixn, pwzName);
719 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
720 }
721 else
722 {
723 // Clear the text value for the node
724 hr = XmlSetText(pixn, L"");
725 ExitOnFailure(hr, "failed to clear text value");
726 }
727 }
728 else if (xaDeleteElement == xa)
729 {
730 // TODO: This may be a little heavy handed
731 hr = XmlRemoveChildren(pixn, pwzName);
732 ExitOnFailure(hr, "failed to delete child node: %ls", pwzName);
733 }
734 else
735 {
736 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
737 }
738 }
739 }
740
741 // Now that we've made all of the changes to this file, save it and move on to the next
742 if (S_OK == hrOpenFailure)
743 {
744 if (fPreserveDate)
745 {
746 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
747 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
748 }
749
750 int iSaveAttempt = 0;
751
752 do
753 {
754 hr = XmlSaveDocument(pixd, pwzFile);
755 if (FAILED(hr))
756 {
757 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
758 switch (id)
759 {
760 case IDABORT:
761 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
762 case IDRETRY:
763 hr = S_FALSE; // hit me, baby, one more time
764 break;
765 case IDIGNORE:
766 hr = S_OK; // pretend everything is okay and bail
767 break;
768 case 0: // No UI case, MsiProcessMessage returns 0
769 if (STIERR_SHARING_VIOLATION == hr)
770 {
771 // Only in case of sharing violation do we retry 30 times, once a second.
772 if (iSaveAttempt < 30)
773 {
774 hr = S_FALSE;
775 ++iSaveAttempt;
776 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
777 Sleep(1000);
778 }
779 else
780 {
781 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
782 }
783 }
784 break;
785 default: // Unknown error
786 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
787 }
788 }
789 } while (S_FALSE == hr);
790
791 if (fPreserveDate)
792 {
793 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
794 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
795 }
796
797 if (fIsFSRedirectDisabled)
798 {
799 fIsFSRedirectDisabled = FALSE;
800 WcaRevertWow64FSRedirection();
801 }
802 }
803 }
804
805LExit:
806 // Make sure we revert FS Redirection if necessary before exiting
807 if (fIsFSRedirectDisabled)
808 {
809 fIsFSRedirectDisabled = FALSE;
810 WcaRevertWow64FSRedirection();
811 }
812#ifndef _WIN64
813 WcaFinalizeWow64();
814#endif
815
816 ReleaseStr(pwzCustomActionData);
817 ReleaseStr(pwzData);
818 ReleaseStr(pwzFile);
819 ReleaseStr(pwzXPath);
820 ReleaseStr(pwzName);
821 ReleaseStr(pwzValue);
822 ReleaseBSTR(bstrProperty);
823 ReleaseVariant(varValue);
824
825 ReleaseObject(pixdDocument2);
826 ReleaseObject(pixn);
827 ReleaseObject(pixd);
828 ReleaseObject(pixnNewNode);
829 ReleaseObject(pixNodes);
830
831 XmlUninitialize();
832
833 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
834}
835
836
837/******************************************************************
838 ExecXmlFileRollback - entry point for XmlFile rollback Custom Action
839
840*******************************************************************/
841extern "C" UINT __stdcall ExecXmlFileRollback(
842 __in MSIHANDLE hInstall
843 )
844{
845// AssertSz(FALSE, "debug ExecXmlFileRollback");
846 HRESULT hr = S_OK;
847 UINT er = ERROR_SUCCESS;
848
849 int iIs64Bit;
850 BOOL fIs64Bit = FALSE;
851
852 LPWSTR pwzCustomActionData = NULL;
853 LPWSTR pwz = NULL;
854 LPWSTR pwzFileName = NULL;
855 LPBYTE pbData = NULL;
856 DWORD_PTR cbData = 0;
857
858 FILETIME ft;
859
860 HANDLE hFile = INVALID_HANDLE_VALUE;
861
862 // initialize
863 hr = WcaInitialize(hInstall, "ExecXmlFileRollback");
864 ExitOnFailure(hr, "failed to initialize");
865
866
867 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
868 ExitOnFailure(hr, "failed to get CustomActionData");
869
870 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
871
872 pwz = pwzCustomActionData;
873
874 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
875 ExitOnFailure(hr, "failed to read component bitness from custom action data");
876
877 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
878 ExitOnFailure(hr, "failed to read file name from custom action data");
879
880 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
881 ExitOnFailure(hr, "failed to read file contents from custom action data");
882
883#ifndef _WIN64
884 fIs64Bit = (BOOL)iIs64Bit;
885
886 if (fIs64Bit)
887 {
888 hr = WcaInitializeWow64();
889 if (S_FALSE == hr)
890 {
891 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
892 }
893 ExitOnFailure(hr, "failed to initialize Wow64 API");
894
895 if (!WcaIsWow64Process())
896 {
897 hr = E_NOTIMPL;
898 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the custom action process is not running in WOW.");
899 }
900
901 hr = WcaDisableWow64FSRedirection();
902 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
903 }
904#endif
905
906 // Always preserve the modified date on rollback
907 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
908 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
909
910 // Open the file
911 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
912 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
913
914 // Write out the old data
915 hr = FileWriteHandle(hFile, pbData, cbData);
916 ExitOnFailure(hr, "failed to write to file: %ls", pwzFileName);
917
918 ReleaseFile(hFile);
919
920 // Always preserve the modified date on rollback
921 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
922 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
923
924LExit:
925 ReleaseStr(pwzCustomActionData);
926 ReleaseStr(pwzFileName);
927
928 ReleaseFile(hFile);
929
930 if (fIs64Bit)
931 {
932 WcaRevertWow64FSRedirection();
933 WcaFinalizeWow64();
934 }
935
936 ReleaseMem(pbData);
937
938 return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : er);
939}
940
diff --git a/src/ext/Util/ca/caDecor.h b/src/ext/Util/ca/caDecor.h
new file mode 100644
index 00000000..da274650
--- /dev/null
+++ b/src/ext/Util/ca/caDecor.h
@@ -0,0 +1,13 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if defined(_M_ARM64)
6#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_A64"
7#elif defined(_M_AMD64)
8#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X64"
9#elif defined(_M_ARM)
10#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_ARM"
11#else
12#define CUSTOM_ACTION_DECORATION(f) L"Wix4" f L"_X86"
13#endif
diff --git a/src/ext/Util/ca/cost.h b/src/ext/Util/ca/cost.h
new file mode 100644
index 00000000..6507e85d
--- /dev/null
+++ b/src/ext/Util/ca/cost.h
@@ -0,0 +1,9 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5const UINT COST_SECUREOBJECT = 1000;
6const UINT COST_SERVICECONFIG = 1000;
7const UINT COST_XMLFILE = 1000;
8const UINT COST_CLOSEAPP = 500;
9const UINT COST_INTERNETSHORTCUT = 2000;
diff --git a/src/ext/Util/ca/dllmain.cpp b/src/ext/Util/ca/dllmain.cpp
new file mode 100644
index 00000000..35ae6d1c
--- /dev/null
+++ b/src/ext/Util/ca/dllmain.cpp
@@ -0,0 +1,26 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5/********************************************************************
6DllMain - standard entry point for all WiX custom actions
7
8********************************************************************/
9extern "C" BOOL WINAPI DllMain(
10 IN HINSTANCE hInst,
11 IN ULONG ulReason,
12 IN LPVOID)
13{
14 switch(ulReason)
15 {
16 case DLL_PROCESS_ATTACH:
17 WcaGlobalInitialize(hInst);
18 break;
19
20 case DLL_PROCESS_DETACH:
21 WcaGlobalFinalize();
22 break;
23 }
24
25 return TRUE;
26}
diff --git a/src/ext/Util/ca/exitearlywithsuccess.cpp b/src/ext/Util/ca/exitearlywithsuccess.cpp
new file mode 100644
index 00000000..00828329
--- /dev/null
+++ b/src/ext/Util/ca/exitearlywithsuccess.cpp
@@ -0,0 +1,27 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/******************************************************************
7WixExitEarlyWithSuccess - entry point for WixExitEarlyWithSuccess
8 custom action which does nothing except return exit code
9 ERROR_NO_MORE_ITEMS. The Windows Installer documentation at
10 http://msdn.microsoft.com/library/aa368072.aspx indicates that
11 this exit code is not treated as an error. This will cause a
12 calling application to receive a successful return code if
13 this custom action executes. This can be useful for backwards
14 compatibility when an application redistributes an MSI and
15 a future major upgrade is released for that MSI. It should be
16 conditioned on a property set by an entry in the Upgrade table
17 of the MSI that detects newer major upgrades of the same MSI
18 already installed on the system. It should be scheduled after
19 the FindRelatedProducts action so that the property will be
20 set if appropriate.
21********************************************************************/
22extern "C" UINT __stdcall WixExitEarlyWithSuccess(
23 __in MSIHANDLE /*hInstall*/
24 )
25{
26 return ERROR_NO_MORE_ITEMS;
27}
diff --git a/src/ext/Util/ca/netshortcuts.cpp b/src/ext/Util/ca/netshortcuts.cpp
new file mode 100644
index 00000000..06826264
--- /dev/null
+++ b/src/ext/Util/ca/netshortcuts.cpp
@@ -0,0 +1,437 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsShortcutsQuery =
6 L"SELECT `Component_`, `Directory_`, `Name`, `Target`, `Attributes`, `IconFile`, `IconIndex` "
7 L"FROM `Wix4InternetShortcut`";
8enum eShortcutsQuery { esqComponent = 1, esqDirectory, esqFilename, esqTarget, esqAttributes, esqIconFile, esqIconIndex };
9enum eShortcutsAttributes { esaLink = 0, esaURL = 1 };
10
11/******************************************************************
12 WixSchedInternetShortcuts - entry point
13
14********************************************************************/
15extern "C" UINT __stdcall WixSchedInternetShortcuts(
16 __in MSIHANDLE hInstall
17 )
18{
19 HRESULT hr = S_OK;
20 UINT er = ERROR_SUCCESS;
21
22 UINT uiCost = 0;
23
24 PMSIHANDLE hView = NULL;
25 PMSIHANDLE hRec = NULL;
26
27 MSIHANDLE hCreateFolderTable = NULL;
28 MSIHANDLE hCreateFolderColumns = NULL;
29
30 LPWSTR pwzCustomActionData = NULL;
31 LPWSTR pwzComponent = NULL;
32 LPWSTR pwzDirectory = NULL;
33 LPWSTR pwzFilename = NULL;
34 LPWSTR pwzTarget = NULL;
35 LPWSTR pwzShortcutPath = NULL;
36 int iAttr = 0;
37 LPWSTR pwzIconFile = NULL;
38 int iIconIndex = 0;
39 IUniformResourceLocatorW* piURL = NULL;
40 IShellLinkW* piShellLink = NULL;
41 BOOL fInitializedCom = FALSE;
42
43 hr = WcaInitialize(hInstall, "WixSchedInternetShortcuts");
44 ExitOnFailure(hr, "failed to initialize WixSchedInternetShortcuts.");
45
46 // anything to do?
47 if (S_OK != WcaTableExists(L"Wix4InternetShortcut"))
48 {
49 WcaLog(LOGMSG_STANDARD, "Wix4InternetShortcut table doesn't exist, so there are no Internet shortcuts to process");
50 goto LExit;
51 }
52
53 // check to see if we can create a shortcut - Server Core and others may not have a shell registered.
54 hr = ::CoInitialize(NULL);
55 ExitOnFailure(hr, "failed to initialize COM");
56 fInitializedCom = TRUE;
57
58 hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL);
59 if (S_OK != hr)
60 {
61 WcaLog(LOGMSG_STANDARD, "failed to create an instance of IUniformResourceLocatorW, skipping shortcut creation");
62 ExitFunction1(hr = S_OK);
63 }
64
65 hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink);
66 if (S_OK != hr)
67 {
68 WcaLog(LOGMSG_STANDARD, "failed to create an instance of IShellLinkW, skipping shortcut creation");
69 ExitFunction1(hr = S_OK);
70 }
71
72 // query and loop through all the shortcuts
73 hr = WcaOpenExecuteView(vcsShortcutsQuery, &hView);
74 ExitOnFailure(hr, "failed to open view on Wix4InternetShortcut table");
75
76 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
77 {
78 // read column values
79 hr = WcaGetRecordString(hRec, esqComponent, &pwzComponent);
80 ExitOnFailure(hr, "failed to get shortcut component");
81 hr = WcaGetRecordString(hRec, esqDirectory, &pwzDirectory);
82 ExitOnFailure(hr, "failed to get shortcut directory");
83 hr = WcaGetRecordString(hRec, esqFilename, &pwzFilename);
84 ExitOnFailure(hr, "failed to get shortcut filename");
85 hr = WcaGetRecordFormattedString(hRec, esqTarget, &pwzTarget);
86 ExitOnFailure(hr, "failed to get shortcut target");
87 hr = WcaGetRecordInteger(hRec, esqAttributes, &iAttr);
88 ExitOnFailure(hr, "failed to get shortcut attributes");
89 hr = WcaGetRecordFormattedString(hRec, esqIconFile, &pwzIconFile);
90 ExitOnFailure(hr, "failed to get shortcut icon file");
91 hr = WcaGetRecordInteger(hRec, esqIconIndex, &iIconIndex);
92 ExitOnFailure(hr, "failed to get shortcut icon index");
93
94 // skip processing this Wix4InternetShortcut row if the component isn't being configured
95 WCA_TODO todo = WcaGetComponentToDo(pwzComponent);
96 if (WCA_TODO_UNKNOWN == todo)
97 {
98 WcaLog(LOGMSG_VERBOSE, "Skipping shortcut for null-action component '%ls'", pwzComponent);
99 continue;
100 }
101
102 // we need to create the directory where the shortcut is supposed to live; rather
103 // than doing so in our deferred custom action, use the CreateFolder table to have MSI
104 // make (and remove) them on our behalf (including the correct cleanup of parent directories).
105 MSIDBERROR dbError = MSIDBERROR_NOERROR;
106 WcaLog(LOGMSG_STANDARD, "Adding folder '%ls', component '%ls' to the CreateFolder table", pwzDirectory, pwzComponent);
107 hr = WcaAddTempRecord(&hCreateFolderTable, &hCreateFolderColumns, L"CreateFolder", &dbError, 0, 2, pwzDirectory, pwzComponent);
108 if (MSIDBERROR_DUPLICATEKEY == dbError)
109 {
110 WcaLog(LOGMSG_STANDARD, "Folder '%ls' already exists in the CreateFolder table; the above error is harmless", pwzDirectory);
111 hr = S_OK;
112 }
113 ExitOnFailure(hr, "Couldn't add temporary CreateFolder row");
114
115 // only if we're installing/reinstalling do we need to schedule the deferred CA
116 // (uninstallation is handled via permanent RemoveFile rows and temporary CreateFolder rows)
117 if (WCA_TODO_INSTALL == todo || WCA_TODO_REINSTALL == todo)
118 {
119 // turn the Directory_ id into a path
120 hr = WcaGetTargetPath(pwzDirectory, &pwzShortcutPath);
121 ExitOnFailure(hr, "failed to allocate string for shortcut directory");
122
123 // append the shortcut filename
124 hr = StrAllocConcat(&pwzShortcutPath, pwzFilename, 0);
125 ExitOnFailure(hr, "failed to allocate string for shortcut filename");
126
127 // write the shortcut path and target to custom action data for deferred CAs
128 hr = WcaWriteStringToCaData(pwzShortcutPath, &pwzCustomActionData);
129 ExitOnFailure(hr, "failed to write shortcut path to custom action data");
130 hr = WcaWriteStringToCaData(pwzTarget, &pwzCustomActionData);
131 ExitOnFailure(hr, "failed to write shortcut target to custom action data");
132 hr = WcaWriteIntegerToCaData(iAttr, &pwzCustomActionData);
133 ExitOnFailure(hr, "failed to write shortcut attributes to custom action data");
134 hr = WcaWriteStringToCaData(pwzIconFile, &pwzCustomActionData);
135 ExitOnFailure(hr, "failed to write icon file to custom action data");
136 hr = WcaWriteIntegerToCaData(iIconIndex, &pwzCustomActionData);
137 ExitOnFailure(hr, "failed to write icon index to custom action data");
138
139 uiCost += COST_INTERNETSHORTCUT;
140 }
141 }
142
143 if (E_NOMOREITEMS == hr)
144 {
145 hr = S_OK;
146 }
147 ExitOnFailure(hr, "Failure occured while processing Wix4InternetShortcut table");
148
149 // if we have any shortcuts to install
150 if (pwzCustomActionData && *pwzCustomActionData)
151 {
152 // add cost to progress bar
153 hr = WcaProgressMessage(uiCost, TRUE);
154 ExitOnFailure(hr, "failed to extend progress bar for InternetShortcuts");
155
156 // provide custom action data to deferred and rollback CAs
157 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"RollbackInternetShortcuts"), pwzCustomActionData);
158 ExitOnFailure(hr, "failed to set WixRollbackInternetShortcuts rollback custom action data");
159 hr = WcaSetProperty(CUSTOM_ACTION_DECORATION(L"CreateInternetShortcuts"), pwzCustomActionData);
160 ExitOnFailure(hr, "failed to set WixCreateInternetShortcuts custom action data");
161 }
162
163LExit:
164 if (hCreateFolderTable)
165 {
166 ::MsiCloseHandle(hCreateFolderTable);
167 }
168
169 if (hCreateFolderColumns)
170 {
171 ::MsiCloseHandle(hCreateFolderColumns);
172 }
173
174 ReleaseStr(pwzCustomActionData);
175 ReleaseStr(pwzComponent);
176 ReleaseStr(pwzDirectory);
177 ReleaseStr(pwzFilename);
178 ReleaseStr(pwzTarget);
179 ReleaseStr(pwzShortcutPath);
180 ReleaseObject(piShellLink);
181 ReleaseObject(piURL);
182
183 if (fInitializedCom)
184 {
185 ::CoUninitialize();
186 }
187
188 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
189 return WcaFinalize(er);
190}
191
192
193
194/******************************************************************
195 CreateUrl - Creates a shortcut via IUniformResourceLocatorW
196
197*******************************************************************/
198static HRESULT CreateUrl(
199 __in_z LPCWSTR wzTarget,
200 __in_z LPCWSTR wzShortcutPath,
201 __in_z_opt LPCWSTR wzIconPath,
202 __in int iconIndex
203)
204{
205 HRESULT hr = S_OK;
206 IUniformResourceLocatorW* piURL = NULL;
207 IPersistFile* piPersistFile = NULL;
208 IPropertySetStorage* piProperties = NULL;
209 IPropertyStorage* piStorage = NULL;
210
211 // create an internet shortcut object
212 WcaLog(LOGMSG_STANDARD, "Creating IUniformResourceLocatorW shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
213 hr = ::CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IUniformResourceLocatorW, (void**)&piURL);
214 ExitOnFailure(hr, "failed to create an instance of IUniformResourceLocatorW");
215
216 // set shortcut target
217 hr = piURL->SetURL(wzTarget, 0);
218 ExitOnFailure(hr, "failed to set shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
219
220 if (wzIconPath)
221 {
222 WcaLog(LOGMSG_STANDARD, "Adding icon '%ls' index '%d'", wzIconPath, iconIndex);
223
224 hr = piURL->QueryInterface(IID_IPropertySetStorage, (void **)&piProperties);
225 ExitOnFailure(hr, "failed to get IPropertySetStorage for shortcut '%ls'", wzShortcutPath);
226
227 hr = piProperties->Open(FMTID_Intshcut, STGM_WRITE, &piStorage);
228 ExitOnFailure(hr, "failed to open storage for shortcut '%ls'", wzShortcutPath);
229
230 PROPSPEC ppids[2] = { {PRSPEC_PROPID, PID_IS_ICONINDEX}, {PRSPEC_PROPID, PID_IS_ICONFILE} };
231 PROPVARIANT ppvar[2];
232
233 PropVariantInit(ppvar);
234 PropVariantInit(ppvar + 1);
235
236 ppvar[0].vt = VT_I4;
237 ppvar[0].lVal = iconIndex;
238 ppvar[1].vt = VT_LPWSTR;
239 ppvar[1].pwszVal = const_cast<LPWSTR>(wzIconPath);
240
241 hr = piStorage->WriteMultiple(2, ppids, ppvar, 0);
242 ExitOnFailure(hr, "failed to write icon storage for shortcut '%ls'", wzShortcutPath);
243
244 hr = piStorage->Commit(STGC_DEFAULT);
245 ExitOnFailure(hr, "failed to commit icon storage for shortcut '%ls'", wzShortcutPath);
246 }
247
248 // get an IPersistFile and save the shortcut
249 hr = piURL->QueryInterface(IID_IPersistFile, (void**)&piPersistFile);
250 ExitOnFailure(hr, "failed to get IPersistFile for shortcut '%ls'", wzShortcutPath);
251
252 hr = piPersistFile->Save(wzShortcutPath, TRUE);
253 ExitOnFailure(hr, "failed to save shortcut '%ls'", wzShortcutPath);
254
255LExit:
256 ReleaseObject(piPersistFile);
257 ReleaseObject(piURL);
258 ReleaseObject(piStorage);
259 ReleaseObject(piProperties);
260
261 return hr;
262}
263
264/******************************************************************
265 CreateLink - Creates a shortcut via IShellLinkW
266
267*******************************************************************/
268static HRESULT CreateLink(
269 __in_z LPCWSTR wzTarget,
270 __in_z LPCWSTR wzShortcutPath,
271 __in_z_opt LPCWSTR wzIconPath,
272 __in int iconIndex
273)
274{
275 HRESULT hr = S_OK;
276 IShellLinkW* piShellLink = NULL;
277 IPersistFile* piPersistFile = NULL;
278
279 // create an internet shortcut object
280 WcaLog(LOGMSG_STANDARD, "Creating IShellLinkW shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
281 hr = ::CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&piShellLink);
282 ExitOnFailure(hr, "failed to create an instance of IShellLinkW");
283
284 // set shortcut target
285 hr = piShellLink->SetPath(wzTarget);
286 ExitOnFailure(hr, "failed to set shortcut '%ls' target '%ls'", wzShortcutPath, wzTarget);
287
288 if (wzIconPath)
289 {
290 WcaLog(LOGMSG_STANDARD, "Adding icon '%ls' index '%d'", wzIconPath, iconIndex);
291 hr = piShellLink->SetIconLocation(wzIconPath, iconIndex);
292 ExitOnFailure(hr, "failed to set icon for shortcut '%ls'", wzShortcutPath);
293 }
294
295 // get an IPersistFile and save the shortcut
296 hr = piShellLink->QueryInterface(IID_IPersistFile, (void**)&piPersistFile);
297 ExitOnFailure(hr, "failed to get IPersistFile for shortcut '%ls'", wzShortcutPath);
298
299 hr = piPersistFile->Save(wzShortcutPath, TRUE);
300 ExitOnFailure(hr, "failed to save shortcut '%ls'", wzShortcutPath);
301
302LExit:
303 ReleaseObject(piPersistFile);
304 ReleaseObject(piShellLink);
305
306 return hr;
307}
308
309
310
311/******************************************************************
312 WixCreateInternetShortcuts - entry point for Internet shortcuts
313 custom action
314*******************************************************************/
315extern "C" UINT __stdcall WixCreateInternetShortcuts(
316 __in MSIHANDLE hInstall
317 )
318{
319 HRESULT hr = S_OK;
320 UINT er = ERROR_SUCCESS;
321
322 LPWSTR pwz = NULL;
323 LPWSTR pwzCustomActionData = NULL;
324 LPWSTR pwzTarget = NULL;
325 LPWSTR pwzShortcutPath = NULL;
326 LPWSTR pwzIconPath = NULL;
327 BOOL fInitializedCom = FALSE;
328 int iAttr = 0;
329 int iIconIndex = 0;
330
331 // initialize
332 hr = WcaInitialize(hInstall, "WixCreateInternetShortcuts");
333 ExitOnFailure(hr, "failed to initialize WixCreateInternetShortcuts");
334
335 hr = ::CoInitialize(NULL);
336 ExitOnFailure(hr, "failed to initialize COM");
337 fInitializedCom = TRUE;
338
339 // extract the custom action data
340 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
341 ExitOnFailure(hr, "failed to get CustomActionData");
342
343 // loop through all the custom action data
344 pwz = pwzCustomActionData;
345 while (pwz && *pwz)
346 {
347 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
348 ExitOnFailure(hr, "failed to read shortcut path from custom action data");
349 hr = WcaReadStringFromCaData(&pwz, &pwzTarget);
350 ExitOnFailure(hr, "failed to read shortcut target from custom action data");
351 hr = WcaReadIntegerFromCaData(&pwz, &iAttr);
352 ExitOnFailure(hr, "failed to read shortcut attributes from custom action data");
353 hr = WcaReadStringFromCaData(&pwz, &pwzIconPath);
354 ExitOnFailure(hr, "failed to read shortcut icon path from custom action data");
355 hr = WcaReadIntegerFromCaData(&pwz, &iIconIndex);
356 ExitOnFailure(hr, "failed to read shortcut icon index from custom action data");
357
358 if ((iAttr & esaURL) == esaURL)
359 {
360 hr = CreateUrl(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex);
361 }
362 else
363 {
364 hr = CreateLink(pwzTarget, pwzShortcutPath, pwzIconPath, iIconIndex);
365 }
366 ExitOnFailure(hr, "failed to create Internet shortcut");
367
368 // tick the progress bar
369 hr = WcaProgressMessage(COST_INTERNETSHORTCUT, FALSE);
370 ExitOnFailure(hr, "failed to tick progress bar for shortcut: %ls", pwzShortcutPath);
371 }
372
373LExit:
374 ReleaseStr(pwzCustomActionData);
375 ReleaseStr(pwzTarget);
376 ReleaseStr(pwzShortcutPath);
377
378 if (fInitializedCom)
379 {
380 ::CoUninitialize();
381 }
382
383 er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
384 return WcaFinalize(er);
385}
386
387
388
389/******************************************************************
390 WixRollbackInternetShortcuts - entry point for Internet shortcuts
391 custom action (rollback)
392*******************************************************************/
393extern "C" UINT __stdcall WixRollbackInternetShortcuts(
394 __in MSIHANDLE hInstall
395 )
396{
397 HRESULT hr = S_OK;
398 UINT er = ERROR_SUCCESS;
399
400 LPWSTR pwz = NULL;
401 LPWSTR pwzCustomActionData = NULL;
402 LPWSTR pwzShortcutPath = NULL;
403 int iAttr = 0;
404
405 // initialize
406 hr = WcaInitialize(hInstall, "WixRemoveInternetShortcuts");
407 ExitOnFailure(hr, "failed to initialize WixRemoveInternetShortcuts");
408
409 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
410 ExitOnFailure(hr, "failed to get CustomActionData");
411
412 // loop through all the custom action data
413 pwz = pwzCustomActionData;
414 while (pwz && *pwz)
415 {
416 // extract the custom action data we're interested in
417 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
418 ExitOnFailure(hr, "failed to read shortcut path from custom action data for rollback");
419
420 // delete file
421 hr = FileEnsureDelete(pwzShortcutPath);
422 ExitOnFailure(hr, "failed to delete file '%ls'", pwzShortcutPath);
423
424 // skip over the shortcut target and attributes
425 hr = WcaReadStringFromCaData(&pwz, &pwzShortcutPath);
426 ExitOnFailure(hr, "failed to skip shortcut target from custom action data for rollback");
427 hr = WcaReadIntegerFromCaData(&pwz, &iAttr);
428 ExitOnFailure(hr, "failed to read shortcut attributes from custom action data");
429 }
430
431LExit:
432 ReleaseStr(pwzCustomActionData);
433 ReleaseStr(pwzShortcutPath);
434
435 er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er;
436 return WcaFinalize(er);
437}
diff --git a/src/ext/Util/ca/precomp.h b/src/ext/Util/ca/precomp.h
new file mode 100644
index 00000000..c5d6afe5
--- /dev/null
+++ b/src/ext/Util/ca/precomp.h
@@ -0,0 +1,54 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#if _WIN32_MSI < 150
6#define _WIN32_MSI 150
7#endif
8
9#include <windows.h>
10#include <msiquery.h>
11#include <msidefs.h>
12#include <stierr.h>
13
14#include <strsafe.h>
15
16#include <msxml2.h>
17#include <Iads.h>
18#include <activeds.h>
19#include <lm.h> // NetApi32.lib
20#include <Ntsecapi.h>
21#include <Dsgetdc.h>
22#include <shlobj.h>
23#include <intshcut.h>
24
25#define MAXUINT USHRT_MAX
26
27#include "wcautil.h"
28#include "wcawow64.h"
29#include "wcawrapquery.h"
30#include "aclutil.h"
31#include "dirutil.h"
32#include "fileutil.h"
33#include "memutil.h"
34#include "osutil.h"
35#include "pathutil.h"
36#include "procutil.h"
37#include "shelutil.h"
38#include "strutil.h"
39#include "sczutil.h"
40#include "rmutil.h"
41#include "userutil.h"
42#include "xmlutil.h"
43#include "wiutil.h"
44
45#include "CustomMsiErrors.h"
46
47#include "sca.h"
48#include "scacost.h"
49#include "cost.h"
50#include "scauser.h"
51#include "scasmb.h"
52#include "scasmbexec.h"
53
54#include "caDecor.h"
diff --git a/src/ext/Util/ca/qtexecca.cpp b/src/ext/Util/ca/qtexecca.cpp
new file mode 100644
index 00000000..ddcc812f
--- /dev/null
+++ b/src/ext/Util/ca/qtexecca.cpp
@@ -0,0 +1,316 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define OUTPUT_BUFFER 1024
6
7// These old "CA" prefix names are deprecated, and intended to go away in wix 4.0, only staying now for compatibility reasons
8const LPCWSTR CAQUIET_TIMEOUT_PROPERTY = L"QtExecCmdTimeout";
9const LPCWSTR CAQUIET_ARGUMENTS_PROPERTY = L"QtExecCmdLine";
10const LPCWSTR CAQUIET64_ARGUMENTS_PROPERTY = L"QtExec64CmdLine";
11// end deprecated section
12
13// WixCA name quiet commandline argument properties
14const LPCWSTR WIX_QUIET_ARGUMENTS_PROPERTY = L"WixQuietExecCmdLine";
15const LPCWSTR WIX_QUIET64_ARGUMENTS_PROPERTY = L"WixQuietExec64CmdLine";
16
17// WixCA quiet timeout properties
18const LPCWSTR WIX_QUIET_TIMEOUT_PROPERTY = L"WixQuietExecCmdTimeout";
19const LPCWSTR WIX_QUIET64_TIMEOUT_PROPERTY = L"WixQuietExec64CmdTimeout";
20
21// WixCA silent commandline argument properties
22const LPCWSTR WIX_SILENT_ARGUMENTS_PROPERTY = L"WixSilentExecCmdLine";
23const LPCWSTR WIX_SILENT64_ARGUMENTS_PROPERTY = L"WixSilentExec64CmdLine";
24
25// WixCA silent timeout properties
26const LPCWSTR WIX_SILENT_TIMEOUT_PROPERTY = L"WixSilentExecCmdTimeout";
27const LPCWSTR WIX_SILENT64_TIMEOUT_PROPERTY = L"WixSilentExec64CmdTimeout";
28
29HRESULT BuildCommandLine(
30 __in LPCWSTR wzProperty,
31 __out LPWSTR *ppwzCommand
32 )
33{
34 Assert(ppwzCommand);
35
36 HRESULT hr = S_OK;
37 BOOL fScheduled = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_SCHEDULED);
38 BOOL fRollback = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_ROLLBACK);
39 BOOL fCommit = ::MsiGetMode(WcaGetInstallHandle(), MSIRUNMODE_COMMIT);
40
41 if (fScheduled || fRollback || fCommit)
42 {
43 if (WcaIsPropertySet("CustomActionData"))
44 {
45 hr = WcaGetProperty( L"CustomActionData", ppwzCommand);
46 ExitOnFailure(hr, "Failed to get CustomActionData");
47 }
48 }
49 else if (WcaIsUnicodePropertySet(wzProperty))
50 {
51 hr = WcaGetFormattedProperty(wzProperty, ppwzCommand);
52 ExitOnFailure(hr, "Failed to get %ls", wzProperty);
53 hr = WcaSetProperty(wzProperty, L""); // clear out the property now that we've read it
54 ExitOnFailure(hr, "Failed to set %ls", wzProperty);
55 }
56
57 if (!*ppwzCommand)
58 {
59 ExitOnFailure(hr = E_INVALIDARG, "Failed to get command line data");
60 }
61
62 if (L'"' != **ppwzCommand)
63 {
64 WcaLog(LOGMSG_STANDARD, "Command string must begin with quoted application name.");
65 ExitOnFailure(hr = E_INVALIDARG, "invalid command line property value");
66 }
67
68LExit:
69 return hr;
70}
71
72#define ONEMINUTE 60000
73
74DWORD GetTimeout(LPCWSTR wzPropertyName)
75{
76 DWORD dwTimeout = ONEMINUTE;
77 HRESULT hr = S_OK;
78
79 LPWSTR pwzData = NULL;
80
81 if (WcaIsUnicodePropertySet(wzPropertyName))
82 {
83 hr = WcaGetProperty(wzPropertyName, &pwzData);
84 ExitOnFailure(hr, "Failed to get %ls", wzPropertyName);
85
86 if ((dwTimeout = (DWORD)_wtoi(pwzData)) == 0)
87 {
88 dwTimeout = ONEMINUTE;
89 }
90 }
91
92LExit:
93 ReleaseStr(pwzData);
94
95 return dwTimeout;
96
97}
98
99HRESULT ExecCommon(
100 __in LPCWSTR wzArgumentsProperty,
101 __in LPCWSTR wzTimeoutProperty,
102 __in BOOL fLogCommand,
103 __in BOOL fLogOutput
104 )
105{
106 HRESULT hr = S_OK;
107 LPWSTR pwzCommand = NULL;
108 DWORD dwTimeout = 0;
109
110 hr = BuildCommandLine(wzArgumentsProperty, &pwzCommand);
111 ExitOnFailure(hr, "Failed to get Command Line");
112
113 dwTimeout = GetTimeout(wzTimeoutProperty);
114
115 hr = QuietExec(pwzCommand, dwTimeout, fLogCommand, fLogOutput);
116 ExitOnFailure(hr, "QuietExec Failed");
117
118LExit:
119 ReleaseStr(pwzCommand);
120
121 return hr;
122}
123
124HRESULT ExecCommon64(
125 __in LPCWSTR wzArgumentsProperty,
126 __in LPCWSTR wzTimeoutProperty,
127 __in BOOL fLogCommand,
128 __in BOOL fLogOutput
129 )
130{
131 HRESULT hr = S_OK;
132 LPWSTR pwzCommand = NULL;
133 DWORD dwTimeout = 0;
134#ifndef _WIN64
135 BOOL fIsWow64Initialized = FALSE;
136 BOOL fRedirected = FALSE;
137
138 hr = WcaInitializeWow64();
139 if (S_FALSE == hr)
140 {
141 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
142 }
143 ExitOnFailure(hr, "Failed to intialize WOW64.");
144 fIsWow64Initialized = TRUE;
145
146 hr = WcaDisableWow64FSRedirection();
147 ExitOnFailure(hr, "Failed to enable filesystem redirection.");
148 fRedirected = TRUE;
149#endif
150
151 hr = BuildCommandLine(wzArgumentsProperty, &pwzCommand);
152 ExitOnFailure(hr, "Failed to get Command Line");
153
154 dwTimeout = GetTimeout(wzTimeoutProperty);
155
156 hr = QuietExec(pwzCommand, dwTimeout, fLogCommand, fLogOutput);
157 ExitOnFailure(hr, "QuietExec64 Failed");
158
159LExit:
160 ReleaseStr(pwzCommand);
161
162#ifndef _WIN64
163 if (fRedirected)
164 {
165 WcaRevertWow64FSRedirection();
166 }
167
168 if (fIsWow64Initialized)
169 {
170 WcaFinalizeWow64();
171 }
172#endif
173
174 return hr;
175}
176
177// These two custom actions are deprecated, and should go away in wix v4.0. WixQuietExec replaces this one,
178// and is not intended to have any difference in behavior apart from CA name and property names.
179extern "C" UINT __stdcall CAQuietExec(
180 __in MSIHANDLE hInstall
181 )
182{
183 Assert(hInstall);
184 HRESULT hr = S_OK;
185 UINT er = ERROR_SUCCESS;
186
187 hr = WcaInitialize(hInstall, "CAQuietExec");
188 ExitOnFailure(hr, "Failed to initialize");
189
190 hr = ExecCommon(CAQUIET_ARGUMENTS_PROPERTY, CAQUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
191 ExitOnFailure(hr, "Failed in ExecCommon method");
192
193LExit:
194 if (FAILED(hr))
195 {
196 er = ERROR_INSTALL_FAILURE;
197 }
198
199 return WcaFinalize(er);
200}
201
202// 2nd deprecated custom action name, superseded by WixQuietExec64
203extern "C" UINT __stdcall CAQuietExec64(
204 __in MSIHANDLE hInstall
205 )
206{
207 Assert(hInstall);
208 HRESULT hr = S_OK;
209 UINT er = ERROR_SUCCESS;
210
211 hr = WcaInitialize(hInstall, "CAQuietExec64");
212 ExitOnFailure(hr, "Failed to initialize");
213
214 hr = ExecCommon64(CAQUIET64_ARGUMENTS_PROPERTY, CAQUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
215 ExitOnFailure(hr, "Failed in ExecCommon64 method");
216
217LExit:
218 if (FAILED(hr))
219 {
220 er = ERROR_INSTALL_FAILURE;
221 }
222
223 return WcaFinalize(er);
224}
225
226extern "C" UINT __stdcall WixQuietExec(
227 __in MSIHANDLE hInstall
228 )
229{
230 Assert(hInstall);
231 HRESULT hr = S_OK;
232 UINT er = ERROR_SUCCESS;
233
234 hr = WcaInitialize(hInstall, "WixQuietExec");
235 ExitOnFailure(hr, "Failed to initialize");
236
237 hr = ExecCommon(WIX_QUIET_ARGUMENTS_PROPERTY, WIX_QUIET_TIMEOUT_PROPERTY, TRUE, TRUE);
238 ExitOnFailure(hr, "Failed in ExecCommon method");
239
240LExit:
241 if (FAILED(hr))
242 {
243 er = ERROR_INSTALL_FAILURE;
244 }
245
246 return WcaFinalize(er);
247}
248
249extern "C" UINT __stdcall WixQuietExec64(
250 __in MSIHANDLE hInstall
251 )
252{
253 Assert(hInstall);
254 HRESULT hr = S_OK;
255 UINT er = ERROR_SUCCESS;
256
257 hr = WcaInitialize(hInstall, "WixQuietExec64");
258 ExitOnFailure(hr, "Failed to initialize");
259
260 hr = ExecCommon64(WIX_QUIET64_ARGUMENTS_PROPERTY, WIX_QUIET64_TIMEOUT_PROPERTY, TRUE, TRUE);
261 ExitOnFailure(hr, "Failed in ExecCommon method");
262
263LExit:
264 if (FAILED(hr))
265 {
266 er = ERROR_INSTALL_FAILURE;
267 }
268
269 return WcaFinalize(er);
270}
271
272extern "C" UINT __stdcall WixSilentExec(
273 __in MSIHANDLE hInstall
274 )
275{
276 Assert(hInstall);
277 HRESULT hr = S_OK;
278 UINT er = ERROR_SUCCESS;
279
280 hr = WcaInitialize(hInstall, "WixSilentExec");
281 ExitOnFailure(hr, "Failed to initialize");
282
283 hr = ExecCommon(WIX_SILENT_ARGUMENTS_PROPERTY, WIX_SILENT_TIMEOUT_PROPERTY, FALSE, FALSE);
284 ExitOnFailure(hr, "Failed in ExecCommon method");
285
286LExit:
287 if (FAILED(hr))
288 {
289 er = ERROR_INSTALL_FAILURE;
290 }
291
292 return WcaFinalize(er);
293}
294
295extern "C" UINT __stdcall WixSilentExec64(
296 __in MSIHANDLE hInstall
297 )
298{
299 Assert(hInstall);
300 HRESULT hr = S_OK;
301 UINT er = ERROR_SUCCESS;
302
303 hr = WcaInitialize(hInstall, "WixSilentExec64");
304 ExitOnFailure(hr, "Failed to initialize");
305
306 hr = ExecCommon64(WIX_SILENT64_ARGUMENTS_PROPERTY, WIX_SILENT64_TIMEOUT_PROPERTY, FALSE, FALSE);
307 ExitOnFailure(hr, "Failed in ExecCommon method");
308
309LExit:
310 if (FAILED(hr))
311 {
312 er = ERROR_INSTALL_FAILURE;
313 }
314
315 return WcaFinalize(er);
316}
diff --git a/src/ext/Util/ca/sca.h b/src/ext/Util/ca/sca.h
new file mode 100644
index 00000000..599122ff
--- /dev/null
+++ b/src/ext/Util/ca/sca.h
@@ -0,0 +1,19 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4// user creation attributes definitions
5enum SCAU_ATTRIBUTES
6{
7 SCAU_DONT_EXPIRE_PASSWRD = 0x00000001,
8 SCAU_PASSWD_CANT_CHANGE = 0x00000002,
9 SCAU_PASSWD_CHANGE_REQD_ON_LOGIN = 0x00000004,
10 SCAU_DISABLE_ACCOUNT = 0x00000008,
11 SCAU_FAIL_IF_EXISTS = 0x00000010,
12 SCAU_UPDATE_IF_EXISTS = 0x00000020,
13 SCAU_ALLOW_LOGON_AS_SERVICE = 0x00000040,
14 SCAU_ALLOW_LOGON_AS_BATCH = 0x00000080,
15
16 SCAU_DONT_REMOVE_ON_UNINSTALL = 0x00000100,
17 SCAU_DONT_CREATE_USER = 0x00000200,
18 SCAU_NON_VITAL = 0x00000400,
19}; \ No newline at end of file
diff --git a/src/ext/Util/ca/scacost.h b/src/ext/Util/ca/scacost.h
new file mode 100644
index 00000000..5b215035
--- /dev/null
+++ b/src/ext/Util/ca/scacost.h
@@ -0,0 +1,18 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5const UINT COST_PERFMON_REGISTER = 1000;
6const UINT COST_PERFMON_UNREGISTER = 1000;
7
8const UINT COST_SMB_CREATESMB = 10000;
9const UINT COST_SMB_DROPSMB = 5000;
10const UINT COST_USER_ADD = 10000;
11const UINT COST_USER_DELETE = 10000;
12
13const UINT COST_PERFMONMANIFEST_REGISTER = 1000;
14const UINT COST_PERFMONMANIFEST_UNREGISTER = 1000;
15
16const UINT COST_EVENTMANIFEST_REGISTER = 1000;
17const UINT COST_EVENTMANIFEST_UNREGISTER = 1000;
18
diff --git a/src/ext/Util/ca/scaexec.cpp b/src/ext/Util/ca/scaexec.cpp
new file mode 100644
index 00000000..5845c1b4
--- /dev/null
+++ b/src/ext/Util/ca/scaexec.cpp
@@ -0,0 +1,1082 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/********************************************************************
7 * CreateSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
8 *
9 * Input: deferred CustomActionData -
10 * wzFsKey\twzShareDesc\twzFullPath\tfIntegratedAuth\twzUserName\tnPermissions\twzUserName\tnPermissions...
11 *
12 * ****************************************************************/
13extern "C" UINT __stdcall CreateSmb(MSIHANDLE hInstall)
14{
15//AssertSz(0, "debug CreateSmb");
16 UINT er = ERROR_SUCCESS;
17 HRESULT hr = S_OK;
18
19 LPWSTR pwzData = NULL;
20 LPWSTR pwz = NULL;
21 LPWSTR pwzFsKey = NULL;
22 LPWSTR pwzShareDesc = NULL;
23 LPWSTR pwzDirectory = NULL;
24 int iAccessMode = 0;
25 DWORD nExPermissions = 0;
26 BOOL fIntegratedAuth;
27 LPWSTR pwzExUser = NULL;
28 SCA_SMBP ssp = {0};
29 DWORD dwExUserPerms = 0;
30 DWORD dwCounter = 0;
31 SCA_SMBP_USER_PERMS* pUserPermsList = NULL;
32
33 hr = WcaInitialize(hInstall, "CreateSmb");
34 ExitOnFailure(hr, "failed to initialize");
35
36 hr = WcaGetProperty( L"CustomActionData", &pwzData);
37 ExitOnFailure(hr, "failed to get CustomActionData");
38
39 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
40
41 pwz = pwzData;
42 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
43 ExitOnFailure(hr, "failed to read share name");
44 hr = WcaReadStringFromCaData(&pwz, &pwzShareDesc); // share description
45 ExitOnFailure(hr, "failed to read share name");
46 hr = WcaReadStringFromCaData(&pwz, &pwzDirectory); // full path to share
47 ExitOnFailure(hr, "failed to read share name");
48 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth));
49 ExitOnFailure(hr, "failed to read integrated authentication");
50
51 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&dwExUserPerms));
52 ExitOnFailure(hr, "failed to read count of permissions to set");
53 if(dwExUserPerms > 0)
54 {
55 pUserPermsList = static_cast<SCA_SMBP_USER_PERMS*>(MemAlloc(sizeof(SCA_SMBP_USER_PERMS)*dwExUserPerms, TRUE));
56 ExitOnNull(pUserPermsList, hr, E_OUTOFMEMORY, "failed to allocate memory for permissions structure");
57
58 //Pull out all of the ExUserPerm strings
59 for (dwCounter = 0; dwCounter < dwExUserPerms; ++dwCounter)
60 {
61 hr = WcaReadStringFromCaData(&pwz, &pwzExUser); // user account
62 ExitOnFailure(hr, "failed to read user account");
63 pUserPermsList[dwCounter].wzUser = pwzExUser;
64 pwzExUser = NULL;
65
66 hr = WcaReadIntegerFromCaData(&pwz, &iAccessMode);
67 ExitOnFailure(hr, "failed to read access mode");
68 pUserPermsList[dwCounter].accessMode = (ACCESS_MODE)iAccessMode;
69 iAccessMode = 0;
70
71 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&nExPermissions));
72 ExitOnFailure(hr, "failed to read count of permissions");
73 pUserPermsList[dwCounter].nPermissions = nExPermissions;
74 nExPermissions = 0;
75 }
76 }
77
78 ssp.wzKey = pwzFsKey;
79 ssp.wzDescription = pwzShareDesc;
80 ssp.wzDirectory = pwzDirectory;
81 ssp.fUseIntegratedAuth = fIntegratedAuth;
82 ssp.dwUserPermissionCount = dwExUserPerms;
83 ssp.pUserPerms = pUserPermsList;
84
85 hr = ScaEnsureSmbExists(&ssp);
86 MessageExitOnFailure(hr, msierrSMBFailedCreate, "failed to create share: '%ls'", pwzFsKey);
87
88 hr = WcaProgressMessage(COST_SMB_CREATESMB, FALSE);
89
90LExit:
91 ReleaseStr(pwzFsKey);
92 ReleaseStr(pwzShareDesc);
93 ReleaseStr(pwzDirectory);
94 ReleaseStr(pwzData);
95
96 if (pUserPermsList)
97 {
98 MemFree(pUserPermsList);
99 }
100
101 if (FAILED(hr))
102 {
103 er = ERROR_INSTALL_FAILURE;
104 }
105 return WcaFinalize(er);
106}
107
108
109
110/********************************************************************
111 DropSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
112
113 Input: deferred CustomActionData - wzFsKey\twzShareDesc\twzFullPath\tnPermissions\tfIntegratedAuth\twzUserName\twzPassword
114
115 * ****************************************************************/
116extern "C" UINT __stdcall DropSmb(MSIHANDLE hInstall)
117{
118 //AssertSz(0, "debug DropSmb");
119 UINT er = ERROR_SUCCESS;
120 HRESULT hr = S_OK;
121
122 LPWSTR pwzData = NULL;
123 LPWSTR pwz = NULL;
124 LPWSTR pwzFsKey = NULL;
125 SCA_SMBP ssp = {0};
126
127 hr = WcaInitialize(hInstall, "DropSmb");
128 ExitOnFailure(hr, "failed to initialize");
129
130 hr = WcaGetProperty( L"CustomActionData", &pwzData);
131 ExitOnFailure(hr, "failed to get CustomActionData");
132
133 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
134
135 pwz = pwzData;
136 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
137 ExitOnFailure(hr, "failed to read share name");
138
139 ssp.wzKey = pwzFsKey;
140
141 hr = ScaDropSmb(&ssp);
142 MessageExitOnFailure(hr, msierrSMBFailedDrop, "failed to delete share: '%ls'", pwzFsKey);
143
144 hr = WcaProgressMessage(COST_SMB_DROPSMB, FALSE);
145
146LExit:
147 ReleaseStr(pwzFsKey);
148 ReleaseStr(pwzData);
149
150 if (FAILED(hr))
151 {
152 er = ERROR_INSTALL_FAILURE;
153 }
154 return WcaFinalize(er);
155}
156
157
158static HRESULT AddUserToGroup(
159 __in LPWSTR wzUser,
160 __in LPCWSTR wzUserDomain,
161 __in LPCWSTR wzGroup,
162 __in LPCWSTR wzGroupDomain
163 )
164{
165 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
166
167 HRESULT hr = S_OK;
168 IADsGroup *pGroup = NULL;
169 BSTR bstrUser = NULL;
170 BSTR bstrGroup = NULL;
171 LPCWSTR wz = NULL;
172 LPWSTR pwzUser = NULL;
173 LOCALGROUP_MEMBERS_INFO_3 lgmi;
174
175 if (*wzGroupDomain)
176 {
177 wz = wzGroupDomain;
178 }
179
180 // Try adding it to the global group first
181 UINT ui = ::NetGroupAddUser(wz, wzGroup, wzUser);
182 if (NERR_GroupNotFound == ui)
183 {
184 // Try adding it to the local group
185 if (wzUserDomain)
186 {
187 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
188 ExitOnFailure(hr, "failed to allocate user domain string");
189 }
190
191 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
192 ui = ::NetLocalGroupAddMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
193 }
194 hr = HRESULT_FROM_WIN32(ui);
195 if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error
196 hr = S_OK;
197
198 //
199 // If we failed, try active directory
200 //
201 if (FAILED(hr))
202 {
203 WcaLog(LOGMSG_VERBOSE, "Failed to add user: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
204
205 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
206 ExitOnFailure(hr, "failed to create user ADsPath for user: %ls domain: %ls", wzUser, wzUserDomain);
207
208 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
209 ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain);
210
211 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
212 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
213
214 hr = pGroup->Add(bstrUser);
215 if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr))
216 hr = S_OK;
217
218 ExitOnFailure(hr, "Failed to add user %ls to group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
219 }
220
221LExit:
222 ReleaseObject(pGroup);
223 ReleaseBSTR(bstrUser);
224 ReleaseBSTR(bstrGroup);
225
226 return hr;
227}
228
229static HRESULT RemoveUserFromGroup(
230 __in LPWSTR wzUser,
231 __in LPCWSTR wzUserDomain,
232 __in LPCWSTR wzGroup,
233 __in LPCWSTR wzGroupDomain
234 )
235{
236 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
237
238 HRESULT hr = S_OK;
239 IADsGroup *pGroup = NULL;
240 BSTR bstrUser = NULL;
241 BSTR bstrGroup = NULL;
242 LPCWSTR wz = NULL;
243 LPWSTR pwzUser = NULL;
244 LOCALGROUP_MEMBERS_INFO_3 lgmi;
245
246 if (*wzGroupDomain)
247 {
248 wz = wzGroupDomain;
249 }
250
251 // Try removing it from the global group first
252 UINT ui = ::NetGroupDelUser(wz, wzGroup, wzUser);
253 if (NERR_GroupNotFound == ui)
254 {
255 // Try removing it from the local group
256 if (wzUserDomain)
257 {
258 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
259 ExitOnFailure(hr, "failed to allocate user domain string");
260 }
261
262 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
263 ui = ::NetLocalGroupDelMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
264 }
265 hr = HRESULT_FROM_WIN32(ui);
266
267 //
268 // If we failed, try active directory
269 //
270 if (FAILED(hr))
271 {
272 WcaLog(LOGMSG_VERBOSE, "Failed to remove user: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
273
274 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
275 ExitOnFailure(hr, "failed to create user ADsPath in order to remove user: %ls domain: %ls from a group", wzUser, wzUserDomain);
276
277 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
278 ExitOnFailure(hr, "failed to create group ADsPath in order to remove user from group: %ls domain: %ls", wzGroup, wzGroupDomain);
279
280 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
281 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
282
283 hr = pGroup->Remove(bstrUser);
284 ExitOnFailure(hr, "Failed to remove user %ls from group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
285 }
286
287LExit:
288 ReleaseObject(pGroup);
289 ReleaseBSTR(bstrUser);
290 ReleaseBSTR(bstrGroup);
291
292 return hr;
293}
294
295
296static HRESULT GetUserHasRight(
297 __in LSA_HANDLE hPolicy,
298 __in PSID pUserSid,
299 __in LPWSTR wzRight,
300 __out BOOL* fHasRight
301)
302{
303 HRESULT hr = S_OK;
304 NTSTATUS nt = 0;
305 LSA_UNICODE_STRING lucPrivilege = { 0 };
306 PLSA_ENUMERATION_INFORMATION rgSids = NULL;
307 ULONG cSids = 0;
308 *fHasRight = FALSE;
309
310 lucPrivilege.Buffer = wzRight;
311 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
312 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
313
314 nt = ::LsaEnumerateAccountsWithUserRight(hPolicy, &lucPrivilege, reinterpret_cast<PVOID*>(&rgSids), &cSids);
315 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
316 ExitOnFailure(hr, "Failed to enumerate users for right: %ls", lucPrivilege.Buffer);
317
318 for (DWORD i = 0; i < cSids; ++i)
319 {
320 PLSA_ENUMERATION_INFORMATION pInfo = rgSids + i;
321 if (::EqualSid(pUserSid, pInfo->Sid))
322 {
323 *fHasRight = TRUE;
324 break;
325 }
326 }
327
328LExit:
329 if (rgSids)
330 {
331 ::LsaFreeMemory(rgSids);
332 }
333
334 return hr;
335}
336
337
338static HRESULT GetExistingUserRightsAssignments(
339 __in_opt LPCWSTR wzDomain,
340 __in LPCWSTR wzName,
341 __inout int* iAttributes
342)
343{
344 HRESULT hr = S_OK;
345 NTSTATUS nt = 0;
346 BOOL fHasRight = FALSE;
347
348 LSA_HANDLE hPolicy = NULL;
349 LSA_OBJECT_ATTRIBUTES objectAttributes = { 0 };
350
351 LPWSTR pwzUser = NULL;
352 PSID psid = NULL;
353
354 if (wzDomain && *wzDomain)
355 {
356 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
357 ExitOnFailure(hr, "Failed to allocate user with domain string");
358 }
359 else
360 {
361 hr = StrAllocString(&pwzUser, wzName, 0);
362 ExitOnFailure(hr, "Failed to allocate string from user name.");
363 }
364
365 hr = AclGetAccountSid(NULL, pwzUser, &psid);
366 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
367
368 nt = ::LsaOpenPolicy(NULL, &objectAttributes, POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy);
369 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
370 ExitOnFailure(hr, "Failed to open LSA policy store");
371
372 hr = GetUserHasRight(hPolicy, psid, L"SeServiceLogonRight", &fHasRight);
373 ExitOnFailure(hr, "Failed to check LogonAsService right");
374
375 if (fHasRight)
376 {
377 *iAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
378 }
379
380 hr = GetUserHasRight(hPolicy, psid, L"SeBatchLogonRight", &fHasRight);
381 ExitOnFailure(hr, "Failed to check LogonAsBatchJob right");
382
383 if (fHasRight)
384 {
385 *iAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
386 }
387
388LExit:
389 if (hPolicy)
390 {
391 ::LsaClose(hPolicy);
392 }
393
394 ReleaseSid(psid);
395 ReleaseStr(pwzUser);
396 return hr;
397}
398
399
400static HRESULT ModifyUserLocalServiceRight(
401 __in_opt LPCWSTR wzDomain,
402 __in LPCWSTR wzName,
403 __in BOOL fAdd
404 )
405{
406 HRESULT hr = S_OK;
407 NTSTATUS nt = 0;
408
409 LPWSTR pwzUser = NULL;
410 PSID psid = NULL;
411 LSA_HANDLE hPolicy = NULL;
412 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
413 LSA_UNICODE_STRING lucPrivilege = { 0 };
414
415 if (wzDomain && *wzDomain)
416 {
417 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
418 ExitOnFailure(hr, "Failed to allocate user with domain string");
419 }
420 else
421 {
422 hr = StrAllocString(&pwzUser, wzName, 0);
423 ExitOnFailure(hr, "Failed to allocate string from user name.");
424 }
425
426 hr = AclGetAccountSid(NULL, pwzUser, &psid);
427 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
428
429 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
430 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
431 ExitOnFailure(hr, "Failed to open LSA policy store.");
432
433 lucPrivilege.Buffer = L"SeServiceLogonRight";
434 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
435 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
436
437 if (fAdd)
438 {
439 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
440 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
441 ExitOnFailure(hr, "Failed to add 'logon as service' bit to user: %ls", pwzUser);
442 }
443 else
444 {
445 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
446 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
447 ExitOnFailure(hr, "Failed to remove 'logon as service' bit from user: %ls", pwzUser);
448 }
449
450LExit:
451 if (hPolicy)
452 {
453 ::LsaClose(hPolicy);
454 }
455
456 ReleaseSid(psid);
457 ReleaseStr(pwzUser);
458 return hr;
459}
460
461
462static HRESULT ModifyUserLocalBatchRight(
463 __in_opt LPCWSTR wzDomain,
464 __in LPCWSTR wzName,
465 __in BOOL fAdd
466 )
467{
468 HRESULT hr = S_OK;
469 NTSTATUS nt = 0;
470
471 LPWSTR pwzUser = NULL;
472 PSID psid = NULL;
473 LSA_HANDLE hPolicy = NULL;
474 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
475 LSA_UNICODE_STRING lucPrivilege = { 0 };
476
477 if (wzDomain && *wzDomain)
478 {
479 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
480 ExitOnFailure(hr, "Failed to allocate user with domain string");
481 }
482 else
483 {
484 hr = StrAllocString(&pwzUser, wzName, 0);
485 ExitOnFailure(hr, "Failed to allocate string from user name.");
486 }
487
488 hr = AclGetAccountSid(NULL, pwzUser, &psid);
489 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
490
491 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
492 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
493 ExitOnFailure(hr, "Failed to open LSA policy store.");
494
495 lucPrivilege.Buffer = L"SeBatchLogonRight";
496 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
497 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
498
499 if (fAdd)
500 {
501 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
502 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
503 ExitOnFailure(hr, "Failed to add 'logon as batch job' bit to user: %ls", pwzUser);
504 }
505 else
506 {
507 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
508 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
509 ExitOnFailure(hr, "Failed to remove 'logon as batch job' bit from user: %ls", pwzUser);
510 }
511
512 LExit:
513 if (hPolicy)
514 {
515 ::LsaClose(hPolicy);
516 }
517
518 ReleaseSid(psid);
519 ReleaseStr(pwzUser);
520 return hr;
521}
522
523static void SetUserPasswordAndAttributes(
524 __in USER_INFO_1* puserInfo,
525 __in LPWSTR wzPassword,
526 __in int iAttributes
527 )
528{
529 Assert(puserInfo);
530
531 // Set the User's password
532 puserInfo->usri1_password = wzPassword;
533
534 // Apply the Attributes
535 if (SCAU_DONT_EXPIRE_PASSWRD & iAttributes)
536 {
537 puserInfo->usri1_flags |= UF_DONT_EXPIRE_PASSWD;
538 }
539 else
540 {
541 puserInfo->usri1_flags &= ~UF_DONT_EXPIRE_PASSWD;
542 }
543
544 if (SCAU_PASSWD_CANT_CHANGE & iAttributes)
545 {
546 puserInfo->usri1_flags |= UF_PASSWD_CANT_CHANGE;
547 }
548 else
549 {
550 puserInfo->usri1_flags &= ~UF_PASSWD_CANT_CHANGE;
551 }
552
553 if (SCAU_DISABLE_ACCOUNT & iAttributes)
554 {
555 puserInfo->usri1_flags |= UF_ACCOUNTDISABLE;
556 }
557 else
558 {
559 puserInfo->usri1_flags &= ~UF_ACCOUNTDISABLE;
560 }
561
562 if (SCAU_PASSWD_CHANGE_REQD_ON_LOGIN & iAttributes) // TODO: for some reason this doesn't work
563 {
564 puserInfo->usri1_flags |= UF_PASSWORD_EXPIRED;
565 }
566 else
567 {
568 puserInfo->usri1_flags &= ~UF_PASSWORD_EXPIRED;
569 }
570}
571
572
573static HRESULT RemoveUserInternal(
574 LPWSTR wzGroupCaData,
575 LPWSTR wzDomain,
576 LPWSTR wzName,
577 int iAttributes
578)
579{
580 HRESULT hr = S_OK;
581 UINT er = ERROR_SUCCESS;
582
583 LPWSTR pwz = NULL;
584 LPWSTR pwzGroup = NULL;
585 LPWSTR pwzGroupDomain = NULL;
586 LPCWSTR wz = NULL;
587 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
588
589 //
590 // Remove the logon as service privilege.
591 //
592 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
593 {
594 hr = ModifyUserLocalServiceRight(wzDomain, wzName, FALSE);
595 if (FAILED(hr))
596 {
597 WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
598 hr = S_OK;
599 }
600 }
601
602 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
603 {
604 hr = ModifyUserLocalBatchRight(wzDomain, wzName, FALSE);
605 if (FAILED(hr))
606 {
607 WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing...");
608 hr = S_OK;
609 }
610 }
611
612 //
613 // Remove the User Account if the user was created by us.
614 //
615 if (!(SCAU_DONT_CREATE_USER & iAttributes))
616 {
617 if (wzDomain && *wzDomain)
618 {
619 er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
620 if (RPC_S_SERVER_UNAVAILABLE == er)
621 {
622 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
623 er = ::DsGetDcNameW(NULL, (LPCWSTR)wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
624 }
625 if (ERROR_SUCCESS == er)
626 {
627 wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
628 }
629 else
630 {
631 wz = wzDomain;
632 }
633 }
634
635 er = ::NetUserDel(wz, wzName);
636 if (NERR_UserNotFound == er)
637 {
638 er = NERR_Success;
639 }
640 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", wzName);
641 }
642 else
643 {
644 //
645 // Remove the user from the groups
646 //
647 pwz = wzGroupCaData;
648 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
649 {
650 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
651
652 if (FAILED(hr))
653 {
654 WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup);
655 }
656 else
657 {
658 hr = RemoveUserFromGroup(wzName, wzDomain, pwzGroup, pwzGroupDomain);
659 if (FAILED(hr))
660 {
661 WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", wzName, pwzGroup);
662 }
663 }
664 }
665
666 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
667 {
668 hr = S_OK;
669 }
670
671 ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", wzName);
672 }
673
674LExit:
675 if (pDomainControllerInfo)
676 {
677 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
678 }
679
680 return hr;
681}
682
683
684/********************************************************************
685 CreateUser - CUSTOM ACTION ENTRY POINT for creating users
686
687 Input: deferred CustomActionData - UserName\tDomain\tPassword\tAttributes\tGroupName\tDomain\tGroupName\tDomain...
688 * *****************************************************************/
689extern "C" UINT __stdcall CreateUser(
690 __in MSIHANDLE hInstall
691 )
692{
693 //AssertSz(0, "Debug CreateUser");
694
695 HRESULT hr = S_OK;
696 UINT er = ERROR_SUCCESS;
697
698 LPWSTR pwzData = NULL;
699 LPWSTR pwz = NULL;
700 LPWSTR pwzName = NULL;
701 LPWSTR pwzDomain = NULL;
702 LPWSTR pwzScriptKey = NULL;
703 LPWSTR pwzPassword = NULL;
704 LPWSTR pwzGroup = NULL;
705 LPWSTR pwzGroupDomain = NULL;
706 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
707 int iAttributes = 0;
708 BOOL fInitializedCom = FALSE;
709
710 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
711 int iOriginalAttributes = 0;
712 int iRollbackAttributes = 0;
713
714 USER_INFO_1 userInfo;
715 USER_INFO_1* puserInfo = NULL;
716 DWORD dw;
717 LPCWSTR wz = NULL;
718
719 hr = WcaInitialize(hInstall, "CreateUser");
720 ExitOnFailure(hr, "failed to initialize");
721
722 hr = ::CoInitialize(NULL);
723 ExitOnFailure(hr, "failed to initialize COM");
724 fInitializedCom = TRUE;
725
726 hr = WcaGetProperty( L"CustomActionData", &pwzData);
727 ExitOnFailure(hr, "failed to get CustomActionData");
728
729 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
730
731 //
732 // Read in the CustomActionData
733 //
734 pwz = pwzData;
735 hr = WcaReadStringFromCaData(&pwz, &pwzName);
736 ExitOnFailure(hr, "failed to read user name from custom action data");
737
738 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
739 ExitOnFailure(hr, "failed to read domain from custom action data");
740
741 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
742 ExitOnFailure(hr, "failed to read attributes from custom action data");
743
744 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
745 ExitOnFailure(hr, "failed to read encoding key from custom action data");
746
747 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
748 ExitOnFailure(hr, "failed to read password from custom action data");
749
750 // There is no rollback scheduled if the key is empty.
751 // Best effort to get original configuration and save it in the script so rollback can restore it.
752 if (*pwzScriptKey)
753 {
754 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
755 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
756
757 iRollbackAttributes = 0;
758 hr = GetExistingUserRightsAssignments(pwzDomain, pwzName, &iOriginalAttributes);
759 if (FAILED(hr))
760 {
761 WcaLogError(hr, "failed to get existing user rights: %ls, continuing anyway.", pwzName);
762 }
763 else
764 {
765 if (!(SCAU_ALLOW_LOGON_AS_SERVICE & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes))
766 {
767 iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
768 }
769 if (!(SCAU_ALLOW_LOGON_AS_BATCH & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes))
770 {
771 iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
772 }
773 }
774
775 hr = WcaCaScriptWriteNumber(hRollbackScript, iRollbackAttributes);
776 ExitOnFailure(hr, "Failed to add data to rollback script.");
777
778 // Nudge the system to get all our rollback data written to disk.
779 WcaCaScriptFlush(hRollbackScript);
780 }
781
782 if (!(SCAU_DONT_CREATE_USER & iAttributes))
783 {
784 ::ZeroMemory(&userInfo, sizeof(USER_INFO_1));
785 userInfo.usri1_name = pwzName;
786 userInfo.usri1_priv = USER_PRIV_USER;
787 userInfo.usri1_flags = UF_SCRIPT;
788 userInfo.usri1_home_dir = NULL;
789 userInfo.usri1_comment = NULL;
790 userInfo.usri1_script_path = NULL;
791
792 SetUserPasswordAndAttributes(&userInfo, pwzPassword, iAttributes);
793
794 //
795 // Create the User
796 //
797 if (pwzDomain && *pwzDomain)
798 {
799 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, NULL, &pDomainControllerInfo );
800 if (RPC_S_SERVER_UNAVAILABLE == er)
801 {
802 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
803 er = ::DsGetDcNameW( NULL, (LPCWSTR)pwzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo );
804 }
805 if (ERROR_SUCCESS == er)
806 {
807 wz = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
808 }
809 else
810 {
811 wz = pwzDomain;
812 }
813 }
814
815 er = ::NetUserAdd(wz, 1, reinterpret_cast<LPBYTE>(&userInfo), &dw);
816 if (NERR_UserExists == er)
817 {
818 if (SCAU_UPDATE_IF_EXISTS & iAttributes)
819 {
820 er = ::NetUserGetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE*>(&puserInfo));
821 if (NERR_Success == er)
822 {
823 // Change the existing user's password and attributes again then try
824 // to update user with this new data
825 SetUserPasswordAndAttributes(puserInfo, pwzPassword, iAttributes);
826
827 er = ::NetUserSetInfo(wz, pwzName, 1, reinterpret_cast<LPBYTE>(puserInfo), &dw);
828 }
829 }
830 else if (!(SCAU_FAIL_IF_EXISTS & iAttributes))
831 {
832 er = NERR_Success;
833 }
834 }
835 else if (NERR_PasswordTooShort == er || NERR_PasswordTooLong == er)
836 {
837 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreatePswd, "failed to create user: %ls due to invalid password.", pwzName);
838 }
839 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreate, "failed to create user: %ls", pwzName);
840 }
841
842 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
843 {
844 hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, TRUE);
845 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as service rights to user: %ls", pwzName);
846 }
847
848 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
849 {
850 hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, TRUE);
851 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as batch job rights to user: %ls", pwzName);
852 }
853
854 //
855 // Add the users to groups
856 //
857 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
858 {
859 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
860 ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup);
861
862 hr = AddUserToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain);
863 MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add user: %ls to group %ls", pwzName, pwzGroup);
864 }
865 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
866 {
867 hr = S_OK;
868 }
869 ExitOnFailure(hr, "failed to get next group in which to include user:%ls", pwzName);
870
871LExit:
872 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
873
874 if (puserInfo)
875 {
876 ::NetApiBufferFree((LPVOID)puserInfo);
877 }
878
879 if (pDomainControllerInfo)
880 {
881 ::NetApiBufferFree((LPVOID)pDomainControllerInfo);
882 }
883
884 ReleaseStr(pwzData);
885 ReleaseStr(pwzName);
886 ReleaseStr(pwzDomain);
887 ReleaseStr(pwzScriptKey);
888 ReleaseStr(pwzPassword);
889 ReleaseStr(pwzGroup);
890 ReleaseStr(pwzGroupDomain);
891
892 if (fInitializedCom)
893 {
894 ::CoUninitialize();
895 }
896
897 if (SCAU_NON_VITAL & iAttributes)
898 {
899 er = ERROR_SUCCESS;
900 }
901 else if (FAILED(hr))
902 {
903 er = ERROR_INSTALL_FAILURE;
904 }
905
906 return WcaFinalize(er);
907}
908
909
910/********************************************************************
911 CreateUserRollback - CUSTOM ACTION ENTRY POINT for CreateUser rollback
912
913 * *****************************************************************/
914extern "C" UINT __stdcall CreateUserRollback(
915 MSIHANDLE hInstall
916)
917{
918 //AssertSz(0, "Debug CreateUserRollback");
919
920 HRESULT hr = S_OK;
921 UINT er = ERROR_SUCCESS;
922
923 LPWSTR pwzData = NULL;
924 LPWSTR pwz = NULL;
925 LPWSTR pwzName = NULL;
926 LPWSTR pwzDomain = NULL;
927 LPWSTR pwzScriptKey = NULL;
928 int iAttributes = 0;
929 BOOL fInitializedCom = FALSE;
930
931 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
932 LPWSTR pwzRollbackData = NULL;
933 int iOriginalAttributes = 0;
934
935 hr = WcaInitialize(hInstall, "CreateUserRollback");
936 ExitOnFailure(hr, "failed to initialize");
937
938 hr = ::CoInitialize(NULL);
939 ExitOnFailure(hr, "failed to initialize COM");
940 fInitializedCom = TRUE;
941
942 hr = WcaGetProperty(L"CustomActionData", &pwzData);
943 ExitOnFailure(hr, "failed to get CustomActionData");
944
945 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
946
947 //
948 // Read in the CustomActionData
949 //
950 pwz = pwzData;
951 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
952 ExitOnFailure(hr, "failed to read encoding key from custom action data");
953
954 hr = WcaReadStringFromCaData(&pwz, &pwzName);
955 ExitOnFailure(hr, "failed to read name from custom action data");
956
957 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
958 ExitOnFailure(hr, "failed to read domain from custom action data");
959
960 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
961 ExitOnFailure(hr, "failed to read attributes from custom action data");
962
963 // Best effort to read original configuration from CreateUser.
964 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
965 if (FAILED(hr))
966 {
967 WcaLogError(hr, "Failed to open rollback CustomAction script, continuing anyway.");
968 }
969 else
970 {
971 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzRollbackData);
972 if (FAILED(hr))
973 {
974 WcaLogError(hr, "Failed to read rollback script into CustomAction data, continuing anyway.");
975 }
976 else
977 {
978 WcaLog(LOGMSG_TRACEONLY, "Rollback Data: %ls", pwzRollbackData);
979
980 pwz = pwzRollbackData;
981 hr = WcaReadIntegerFromCaData(&pwz, &iOriginalAttributes);
982 if (FAILED(hr))
983 {
984 WcaLogError(hr, "failed to read attributes from rollback data, continuing anyway");
985 }
986 else
987 {
988 iAttributes |= iOriginalAttributes;
989 }
990 }
991 }
992
993 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
994
995LExit:
996 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
997
998 ReleaseStr(pwzData);
999 ReleaseStr(pwzName);
1000 ReleaseStr(pwzDomain);
1001 ReleaseStr(pwzScriptKey);
1002 ReleaseStr(pwzRollbackData);
1003
1004 if (fInitializedCom)
1005 {
1006 ::CoUninitialize();
1007 }
1008
1009 if (FAILED(hr))
1010 {
1011 er = ERROR_INSTALL_FAILURE;
1012 }
1013
1014 return WcaFinalize(er);
1015}
1016
1017
1018/********************************************************************
1019 RemoveUser - CUSTOM ACTION ENTRY POINT for removing users
1020
1021 Input: deferred CustomActionData - Name\tDomain
1022 * *****************************************************************/
1023extern "C" UINT __stdcall RemoveUser(
1024 MSIHANDLE hInstall
1025)
1026{
1027 //AssertSz(0, "Debug RemoveUser");
1028
1029 HRESULT hr = S_OK;
1030 UINT er = ERROR_SUCCESS;
1031
1032 LPWSTR pwzData = NULL;
1033 LPWSTR pwz = NULL;
1034 LPWSTR pwzName = NULL;
1035 LPWSTR pwzDomain = NULL;
1036 int iAttributes = 0;
1037 BOOL fInitializedCom = FALSE;
1038
1039 hr = WcaInitialize(hInstall, "RemoveUser");
1040 ExitOnFailure(hr, "failed to initialize");
1041
1042 hr = ::CoInitialize(NULL);
1043 ExitOnFailure(hr, "failed to initialize COM");
1044 fInitializedCom = TRUE;
1045
1046 hr = WcaGetProperty(L"CustomActionData", &pwzData);
1047 ExitOnFailure(hr, "failed to get CustomActionData");
1048
1049 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
1050
1051 //
1052 // Read in the CustomActionData
1053 //
1054 pwz = pwzData;
1055 hr = WcaReadStringFromCaData(&pwz, &pwzName);
1056 ExitOnFailure(hr, "failed to read name from custom action data");
1057
1058 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
1059 ExitOnFailure(hr, "failed to read domain from custom action data");
1060
1061 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1062 ExitOnFailure(hr, "failed to read attributes from custom action data");
1063
1064 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
1065
1066LExit:
1067 ReleaseStr(pwzData);
1068 ReleaseStr(pwzName);
1069 ReleaseStr(pwzDomain);
1070
1071 if (fInitializedCom)
1072 {
1073 ::CoUninitialize();
1074 }
1075
1076 if (FAILED(hr))
1077 {
1078 er = ERROR_INSTALL_FAILURE;
1079 }
1080
1081 return WcaFinalize(er);
1082}
diff --git a/src/ext/Util/ca/scamanifest.cpp b/src/ext/Util/ca/scamanifest.cpp
new file mode 100644
index 00000000..adb8d3d3
--- /dev/null
+++ b/src/ext/Util/ca/scamanifest.cpp
@@ -0,0 +1,377 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsPerfmonManifestQuery = L"SELECT `Component_`, `File`, `ResourceFileDirectory` FROM `Wix4PerfmonManifest`";
6LPCWSTR vcsEventManifestQuery = L"SELECT `Component_`, `File` FROM `Wix4EventManifest`";
7enum ePerfMonManifestQuery { pfmComponent = 1, pfmFile, pfmResourceFileDir };
8enum eEventManifestQuery { emComponent = 1, emFile};
9
10BOOL IsVistaOrAbove()
11{
12 OSVERSIONINFO osvi;
13 ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
14 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
15 #pragma warning(suppress: 4996) //TODO: use non-deprecated function to check OS version
16 if (!::GetVersionEx(&osvi))
17 {
18 return false;
19 }
20 return osvi.dwMajorVersion >= 6;
21}
22
23
24/********************************************************************
25 ConfigurePerfmonManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
26 Perfmon counter manifest registering
27
28********************************************************************/
29extern "C" UINT __stdcall ConfigurePerfmonManifestRegister(
30 __in MSIHANDLE hInstall
31 )
32{
33 HRESULT hr;
34 UINT er = ERROR_SUCCESS;
35
36 PMSIHANDLE hView, hRec;
37 LPWSTR pwzData = NULL, pwzResourceFilePath = NULL, pwzFile = NULL, pwzCommand = NULL;
38 INSTALLSTATE isInstalled, isAction;
39
40 hr = WcaInitialize(hInstall, "ConfigurePerfmonManifestReg");
41 ExitOnFailure(hr, "Failed to initialize");
42
43 if (!IsVistaOrAbove())
44 {
45 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestRegister() because the target system does not support perfmon manifest");
46 ExitFunction1(hr = S_FALSE);
47 }
48 // check to see if necessary tables are specified
49 if (S_OK != WcaTableExists(L"Wix4PerfmonManifest"))
50 {
51 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestRegister() because Wix4PerfmonManifest table not present");
52 ExitFunction1(hr = S_FALSE);
53 }
54
55 hr = WcaOpenExecuteView(vcsPerfmonManifestQuery, &hView);
56 ExitOnFailure(hr, "failed to open view on PerfMonManifest table");
57 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
58 {
59 // get component install state
60 hr = WcaGetRecordString(hRec, pfmComponent, &pwzData);
61 ExitOnFailure(hr, "failed to get Component for PerfMonManifest");
62 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
63 hr = HRESULT_FROM_WIN32(er);
64 ExitOnFailure(hr, "failed to get Component state for PerfMonManifest");
65 if (!WcaIsInstalling(isInstalled, isAction))
66 {
67 continue;
68 }
69
70 hr = WcaGetRecordFormattedString(hRec, pfmFile, &pwzFile);
71 ExitOnFailure(hr, "failed to get File for PerfMonManifest");
72
73 hr = WcaGetRecordFormattedString(hRec, pfmResourceFileDir, &pwzResourceFilePath);
74 ExitOnFailure(hr, "failed to get ApplicationIdentity for PerfMonManifest");
75 size_t iResourcePath = lstrlenW(pwzResourceFilePath);
76 if ( iResourcePath > 0 && *(pwzResourceFilePath + iResourcePath -1) == L'\\')
77 *(pwzResourceFilePath + iResourcePath -1) = 0; //remove the trailing '\'
78
79 hr = StrAllocFormatted(&pwzCommand, L"\"unlodctr.exe\" /m:\"%s\"", pwzFile);
80 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
81
82 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
83 ExitOnFailure(hr, "failed to schedule RollbackRegisterPerfmonManifest action");
84
85 if ( *pwzResourceFilePath )
86 {
87 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\" \"%s\"", pwzFile, pwzResourceFilePath);
88 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
89 }
90 else
91 {
92 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\"", pwzFile);
93 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
94 }
95
96 WcaLog(LOGMSG_VERBOSE, "RegisterPerfmonManifest's CustomActionData: '%ls'", pwzCommand);
97
98 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
99 ExitOnFailure(hr, "failed to schedule RegisterPerfmonManifest action");
100 }
101
102 if (hr == E_NOMOREITEMS)
103 {
104 hr = S_OK;
105 }
106 ExitOnFailure(hr, "Failure while processing PerfMonManifest");
107
108 hr = S_OK;
109
110LExit:
111 ReleaseStr(pwzData);
112 ReleaseStr(pwzResourceFilePath);
113 ReleaseStr(pwzFile);
114 ReleaseStr(pwzCommand);
115
116 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
117 return WcaFinalize(er);
118}
119
120
121/********************************************************************
122 ConfigurePerfmonUninstall - CUSTOM ACTION ENTRY POINT for uninstalling
123 Perfmon counters
124
125********************************************************************/
126extern "C" UINT __stdcall ConfigurePerfmonManifestUnregister(
127 __in MSIHANDLE hInstall
128 )
129{
130 HRESULT hr;
131 UINT er = ERROR_SUCCESS;
132
133 PMSIHANDLE hView, hRec;
134 LPWSTR pwzData = NULL, pwzResourceFilePath = NULL, pwzFile = NULL, pwzCommand = NULL;
135 INSTALLSTATE isInstalled, isAction;
136
137 hr = WcaInitialize(hInstall, "ConfigurePerfmonManifestUnreg");
138 ExitOnFailure(hr, "Failed to initialize");
139
140 if (!IsVistaOrAbove())
141 {
142 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestUnregister() because the target system does not support perfmon manifest");
143 ExitFunction1(hr = S_FALSE);
144 }
145 // check to see if necessary tables are specified
146 if (WcaTableExists(L"Wix4PerfmonManifest") != S_OK)
147 {
148 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigurePerfmonManifestUnregister() because Wix4PerfmonManifest table not present");
149 ExitFunction1(hr = S_FALSE);
150 }
151
152 hr = WcaOpenExecuteView(vcsPerfmonManifestQuery, &hView);
153 ExitOnFailure(hr, "failed to open view on Wix4PerfmonManifest table");
154 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
155 {
156 // get component install state
157 hr = WcaGetRecordString(hRec, pfmComponent, &pwzData);
158 ExitOnFailure(hr, "failed to get Component for Wix4PerfmonManifest");
159 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
160 hr = HRESULT_FROM_WIN32(er);
161 ExitOnFailure(hr, "failed to get Component state for Wix4PerfmonManifest");
162 if (!WcaIsUninstalling(isInstalled, isAction))
163 {
164 continue;
165 }
166
167 hr = WcaGetRecordFormattedString(hRec, pfmFile, &pwzFile);
168 ExitOnFailure(hr, "failed to get File for Wix4PerfmonManifest");
169
170 hr = WcaGetRecordFormattedString(hRec, pfmResourceFileDir, &pwzResourceFilePath);
171 ExitOnFailure(hr, "failed to get ApplicationIdentity for Wix4PerfmonManifest");
172 size_t iResourcePath = lstrlenW(pwzResourceFilePath);
173 if ( iResourcePath > 0 && *(pwzResourceFilePath + iResourcePath -1) == L'\\')
174 *(pwzResourceFilePath + iResourcePath -1) = 0; //remove the trailing '\'
175
176 hr = StrAllocFormatted(&pwzCommand, L"\"lodctr.exe\" /m:\"%s\" \"%s\"", pwzFile, pwzResourceFilePath);
177 ExitOnFailure(hr, "failed to copy string in Wix4PerfmonManifest");
178
179 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
180 ExitOnFailure(hr, "failed to schedule RollbackUnregisterPerfmonManifest action");
181
182 hr = StrAllocFormatted(&pwzCommand, L"\"unlodctr.exe\" /m:\"%s\"", pwzFile);
183 ExitOnFailure(hr, "failed to copy string in PerfMonManifest");
184
185 WcaLog(LOGMSG_VERBOSE, "UnRegisterPerfmonManifest's CustomActionData: '%ls'", pwzCommand);
186
187 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterPerfmonManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
188 ExitOnFailure(hr, "failed to schedule UnregisterPerfmonManifest action");
189 }
190
191 if (hr == E_NOMOREITEMS)
192 {
193 hr = S_OK;
194 }
195 ExitOnFailure(hr, "Failure while processing PerfMonManifest");
196
197 hr = S_OK;
198
199LExit:
200 ReleaseStr(pwzData);
201 ReleaseStr(pwzResourceFilePath);
202 ReleaseStr(pwzFile);
203 ReleaseStr(pwzCommand);
204
205 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
206 return WcaFinalize(er);
207}
208
209/********************************************************************
210 ConfigureEventManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
211 Event manifest registering
212
213********************************************************************/
214extern "C" UINT __stdcall ConfigureEventManifestRegister(
215 __in MSIHANDLE hInstall
216 )
217{
218 HRESULT hr;
219 UINT er = ERROR_SUCCESS;
220
221 PMSIHANDLE hView, hRec;
222 LPWSTR pwzData = NULL, pwzFile = NULL, pwzCommand = NULL;
223 INSTALLSTATE isInstalled, isAction;
224
225 hr = WcaInitialize(hInstall, "ConfigureEventManifestReg");
226 ExitOnFailure(hr, "Failed to initialize");
227
228 if (!IsVistaOrAbove())
229 {
230 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestRegister() because the target system does not support event manifest");
231 ExitFunction1(hr = S_FALSE);
232 }
233 // check to see if necessary tables are specified
234 if (S_OK != WcaTableExists(L"Wix4EventManifest"))
235 {
236 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestRegister() because Wix4EventManifest table not present");
237 ExitFunction1(hr = S_FALSE);
238 }
239
240 hr = WcaOpenExecuteView(vcsEventManifestQuery, &hView);
241 ExitOnFailure(hr, "failed to open view on Wix4EventManifest table");
242 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
243 {
244 // get component install state
245 hr = WcaGetRecordString(hRec, emComponent, &pwzData);
246 ExitOnFailure(hr, "failed to get Component for Wix4EventManifest");
247 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
248 hr = HRESULT_FROM_WIN32(er);
249 ExitOnFailure(hr, "failed to get Component state for Wix4EventManifest");
250 if (!WcaIsInstalling(isInstalled, isAction))
251 {
252 continue;
253 }
254
255 hr = WcaGetRecordFormattedString(hRec, emFile, &pwzFile);
256 ExitOnFailure(hr, "failed to get File for Wix4EventManifest");
257
258 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" um \"%s\"", pwzFile);
259 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
260
261 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
262 ExitOnFailure(hr, "failed to schedule RollbackRegisterEventManifest action");
263
264 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" im \"%s\"", pwzFile);
265 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
266 WcaLog(LOGMSG_VERBOSE, "RegisterEventManifest's CustomActionData: '%ls'", pwzCommand);
267
268 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterEventManifest"), pwzCommand, COST_EVENTMANIFEST_REGISTER);
269 ExitOnFailure(hr, "failed to schedule RegisterEventManifest action");
270 }
271
272 if (hr == E_NOMOREITEMS)
273 {
274 hr = S_OK;
275 }
276 ExitOnFailure(hr, "Failure while processing Wix4EventManifest");
277
278 hr = S_OK;
279
280LExit:
281 ReleaseStr(pwzData);
282 ReleaseStr(pwzFile);
283 ReleaseStr(pwzCommand);
284
285 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
286 return WcaFinalize(er);
287}
288
289
290
291/********************************************************************
292 ConfigureEventManifestRegister - CUSTOM ACTION ENTRY POINT for scheduling
293 Event manifest registering
294
295********************************************************************/
296extern "C" UINT __stdcall ConfigureEventManifestUnregister(
297 __in MSIHANDLE hInstall
298 )
299{
300 HRESULT hr;
301 UINT er = ERROR_SUCCESS;
302
303 PMSIHANDLE hView, hRec;
304 LPWSTR pwzData = NULL, pwzFile = NULL, pwzCommand = NULL;
305 INSTALLSTATE isInstalled, isAction;
306
307 hr = WcaInitialize(hInstall, "ConfigureEventManifestUnreg");
308 ExitOnFailure(hr, "Failed to initialize");
309
310 if (!IsVistaOrAbove())
311 {
312 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestUnregister() because the target system does not support event manifest");
313 ExitFunction1(hr = S_FALSE);
314 }
315 // check to see if necessary tables are specified
316 if (S_OK != WcaTableExists(L"Wix4EventManifest"))
317 {
318 WcaLog(LOGMSG_VERBOSE, "Skipping ConfigureEventManifestUnregister() because Wix4EventManifest table not present");
319 ExitFunction1(hr = S_FALSE);
320 }
321
322 hr = WcaOpenExecuteView(vcsEventManifestQuery, &hView);
323 ExitOnFailure(hr, "failed to open view on Wix4EventManifest table");
324 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
325 {
326 // get component install state
327 hr = WcaGetRecordString(hRec, emComponent, &pwzData);
328 ExitOnFailure(hr, "failed to get Component for Wix4EventManifest");
329 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
330 hr = HRESULT_FROM_WIN32(er);
331 ExitOnFailure(hr, "failed to get Component state for Wix4EventManifest");
332
333 // nothing to do on an install
334 // schedule the rollback action when reinstalling to re-register pre-patch manifest
335 if (!WcaIsUninstalling(isInstalled, isAction) && !WcaIsReInstalling(isInstalled, isAction))
336 {
337 continue;
338 }
339
340 hr = WcaGetRecordFormattedString(hRec, emFile, &pwzFile);
341 ExitOnFailure(hr, "failed to get File for Wix4EventManifest");
342
343 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" im \"%s\"", pwzFile);
344 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
345
346 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_REGISTER);
347 ExitOnFailure(hr, "failed to schedule RollbackUnregisterEventManifest action");
348
349 // no need to uninstall on a repair/patch. Register action will re-register and update the manifest.
350 if (!WcaIsReInstalling(isInstalled, isAction))
351 {
352 hr = StrAllocFormatted(&pwzCommand, L"\"wevtutil.exe\" um \"%s\"", pwzFile);
353 ExitOnFailure(hr, "failed to copy string in Wix4EventManifest");
354 WcaLog(LOGMSG_VERBOSE, "UnregisterEventManifest's CustomActionData: '%ls'", pwzCommand);
355
356 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterEventManifest"), pwzCommand, COST_PERFMONMANIFEST_UNREGISTER);
357 ExitOnFailure(hr, "failed to schedule UnregisterEventManifest action");
358 }
359 }
360
361 if (hr == E_NOMOREITEMS)
362 {
363 hr = S_OK;
364 }
365 ExitOnFailure(hr, "Failure while processing Wix4EventManifest");
366
367 hr = S_OK;
368
369LExit:
370 ReleaseStr(pwzData);
371 ReleaseStr(pwzFile);
372 ReleaseStr(pwzCommand);
373
374 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
375 return WcaFinalize(er);
376}
377
diff --git a/src/ext/Util/ca/scaperf.cpp b/src/ext/Util/ca/scaperf.cpp
new file mode 100644
index 00000000..fd301278
--- /dev/null
+++ b/src/ext/Util/ca/scaperf.cpp
@@ -0,0 +1,310 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsPerfCounterDataQuery = L"SELECT `Wix4PerformanceCategory`, `Component_`, `Name`, `IniData`, `ConstantData` FROM `Wix4PerformanceCategory`";
6enum ePerfCounterDataQuery { pcdqId = 1, pcdqComponent, pcdqName, pcdqIniData, pcdqConstantData };
7
8LPCWSTR vcsPerfMonQuery = L"SELECT `Component_`, `File`, `Name` FROM `Wix4Perfmon`";
9enum ePerfMonQuery { pmqComponent = 1, pmqFile, pmqName };
10
11
12static HRESULT ProcessPerformanceCategory(
13 __in MSIHANDLE hInstall,
14 __in BOOL fInstall
15 );
16
17
18/********************************************************************
19 InstallPerfCounterData - CUSTOM ACTION ENTRY POINT for installing
20 Performance Counters.
21
22********************************************************************/
23extern "C" UINT __stdcall InstallPerfCounterData(
24 __in MSIHANDLE hInstall
25 )
26{
27 // AssertSz(FALSE, "debug InstallPerfCounterData{}");
28 HRESULT hr;
29 UINT er = ERROR_SUCCESS;
30
31 hr = WcaInitialize(hInstall, "InstallPerfCounterData");
32 ExitOnFailure(hr, "Failed to initialize InstallPerfCounterData.");
33
34 hr = ProcessPerformanceCategory(hInstall, TRUE);
35 MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to process Wix4PerformanceCategory table.");
36
37LExit:
38 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
39 return WcaFinalize(er);
40}
41
42
43/********************************************************************
44 UninstallPerfCounterData - CUSTOM ACTION ENTRY POINT for installing
45 Performance Counters.
46
47********************************************************************/
48extern "C" UINT __stdcall UninstallPerfCounterData(
49 __in MSIHANDLE hInstall
50 )
51{
52 // AssertSz(FALSE, "debug UninstallPerfCounterData{}");
53 HRESULT hr;
54 UINT er = ERROR_SUCCESS;
55
56 hr = WcaInitialize(hInstall, "UninstallPerfCounterData");
57 ExitOnFailure(hr, "Failed to initialize UninstallPerfCounterData.");
58
59 hr = ProcessPerformanceCategory(hInstall, FALSE);
60 MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to process Wix4PerformanceCategory table.");
61
62LExit:
63 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
64 return WcaFinalize(er);
65}
66
67
68/********************************************************************
69 RegisterPerfmon - CUSTOM ACTION ENTRY POINT for installing Perfmon counters
70
71********************************************************************/
72extern "C" UINT __stdcall ConfigurePerfmonInstall(
73 __in MSIHANDLE hInstall
74 )
75{
76// Assert(FALSE);
77 HRESULT hr;
78 UINT er = ERROR_SUCCESS;
79
80 PMSIHANDLE hView, hRec;
81 LPWSTR pwzData = NULL, pwzName = NULL, pwzFile = NULL;
82 INSTALLSTATE isInstalled, isAction;
83
84 hr = WcaInitialize(hInstall, "ConfigurePerfmonInstall");
85 ExitOnFailure(hr, "Failed to initialize");
86
87 // check to see if necessary tables are specified
88 if (S_OK != WcaTableExists(L"Wix4Perfmon"))
89 {
90 WcaLog(LOGMSG_VERBOSE, "Skipping RegisterPerfmon() because Wix4Perfmon table not present");
91 ExitFunction1(hr = S_FALSE);
92 }
93
94 hr = WcaOpenExecuteView(vcsPerfMonQuery, &hView);
95 ExitOnFailure(hr, "failed to open view on PerfMon table");
96 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
97 {
98 // get component install state
99 hr = WcaGetRecordString(hRec, pmqComponent, &pwzData);
100 ExitOnFailure(hr, "failed to get Component for PerfMon");
101 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
102 hr = HRESULT_FROM_WIN32(er);
103 ExitOnFailure(hr, "failed to get Component state for PerfMon");
104 if (!WcaIsInstalling(isInstalled, isAction))
105 {
106 continue;
107 }
108
109 hr = WcaGetRecordString(hRec, pmqName, &pwzName);
110 ExitOnFailure(hr, "failed to get Name for PerfMon");
111
112 hr = WcaGetRecordFormattedString(hRec, pmqFile, &pwzFile);
113 ExitOnFailure(hr, "failed to get File for PerfMon");
114
115 WcaLog(LOGMSG_VERBOSE, "ConfigurePerfmonInstall's CustomActionData: '%ls', '%ls'", pwzName, pwzFile);
116 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterPerfmon"), pwzFile, COST_PERFMON_REGISTER);
117 ExitOnFailure(hr, "failed to schedule RegisterPerfmon action");
118 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterPerfmon"), pwzName, COST_PERFMON_UNREGISTER);
119 ExitOnFailure(hr, "failed to schedule RollbackRegisterPerfmon action");
120 }
121
122 if (hr == E_NOMOREITEMS)
123 {
124 hr = S_OK;
125 }
126 ExitOnFailure(hr, "Failure while processing PerfMon");
127
128 hr = S_OK;
129
130LExit:
131 ReleaseStr(pwzData);
132 ReleaseStr(pwzName);
133 ReleaseStr(pwzFile);
134
135 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
136 return WcaFinalize(er);
137}
138
139
140/********************************************************************
141 ConfigurePerfmonUninstall - CUSTOM ACTION ENTRY POINT for uninstalling
142 Perfmon counters
143
144********************************************************************/
145extern "C" UINT __stdcall ConfigurePerfmonUninstall(
146 __in MSIHANDLE hInstall
147 )
148{
149// Assert(FALSE);
150 HRESULT hr;
151 UINT er = ERROR_SUCCESS;
152
153 PMSIHANDLE hView, hRec;
154 LPWSTR pwzData = NULL, pwzName = NULL, pwzFile = NULL;
155 INSTALLSTATE isInstalled, isAction;
156
157 hr = WcaInitialize(hInstall, "ConfigurePerfmonUninstall");
158 ExitOnFailure(hr, "Failed to initialize");
159
160 // check to see if necessary tables are specified
161 if (WcaTableExists(L"Wix4Perfmon") != S_OK)
162 {
163 WcaLog(LOGMSG_VERBOSE, "Skipping UnregisterPerfmon() because Wix4Perfmon table not present");
164 ExitFunction1(hr = S_FALSE);
165 }
166
167 hr = WcaOpenExecuteView(vcsPerfMonQuery, &hView);
168 ExitOnFailure(hr, "failed to open view on PerfMon table");
169 while ((hr = WcaFetchRecord(hView, &hRec)) == S_OK)
170 {
171 // get component install state
172 hr = WcaGetRecordString(hRec, pmqComponent, &pwzData);
173 ExitOnFailure(hr, "failed to get Component for PerfMon");
174 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
175 hr = HRESULT_FROM_WIN32(er);
176 ExitOnFailure(hr, "failed to get Component state for PerfMon");
177 if (!WcaIsUninstalling(isInstalled, isAction))
178 {
179 continue;
180 }
181
182 hr = WcaGetRecordString(hRec, pmqName, &pwzName);
183 ExitOnFailure(hr, "failed to get Name for PerfMon");
184
185 hr = WcaGetRecordFormattedString(hRec, pmqFile, &pwzFile);
186 ExitOnFailure(hr, "failed to get File for PerfMon");
187
188 WcaLog(LOGMSG_VERBOSE, "ConfigurePerfmonUninstall's CustomActionData: '%ls', '%ls'", pwzName, pwzFile);
189 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterPerfmon"), pwzName, COST_PERFMON_UNREGISTER);
190 ExitOnFailure(hr, "failed to schedule UnregisterPerfmon action");
191 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterPerfmon"), pwzFile, COST_PERFMON_REGISTER);
192 ExitOnFailure(hr, "failed to schedule RollbackUnregisterPerfmon action");
193 }
194
195 if (hr == E_NOMOREITEMS)
196 {
197 hr = S_OK;
198 }
199 ExitOnFailure(hr, "Failure while processing PerfMon");
200
201 hr = S_OK;
202
203LExit:
204 ReleaseStr(pwzData);
205 ReleaseStr(pwzName);
206 ReleaseStr(pwzFile);
207
208 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
209 return WcaFinalize(er);
210}
211
212
213
214static HRESULT ProcessPerformanceCategory(
215 __in MSIHANDLE hInstall,
216 __in BOOL fInstall
217 )
218{
219 HRESULT hr = S_OK;
220 DWORD er = ERROR_SUCCESS;
221
222 PMSIHANDLE hView, hRec;
223 LPWSTR pwzId = NULL;
224 LPWSTR pwzComponent = NULL;
225 LPWSTR pwzName = NULL;
226 LPWSTR pwzData = NULL;
227 INSTALLSTATE isInstalled, isAction;
228
229 LPWSTR pwzCustomActionData = NULL;
230
231 // check to see if necessary tables are specified
232 if (S_OK != WcaTableExists(L"Wix4PerformanceCategory"))
233 {
234 ExitFunction1(hr = S_FALSE);
235 }
236
237 hr = WcaOpenExecuteView(vcsPerfCounterDataQuery, &hView);
238 ExitOnFailure(hr, "failed to open view on Wix4PerformanceCategory table");
239 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
240 {
241 hr = WcaGetRecordString(hRec, pcdqId, &pwzId);
242 ExitOnFailure(hr, "Failed to get id for Wix4PerformanceCategory.");
243
244 // Check to see if the Component is being installed or uninstalled
245 // when we are processing the same.
246 hr = WcaGetRecordString(hRec, pcdqComponent, &pwzComponent);
247 ExitOnFailure(hr, "Failed to get Component for Wix4PerformanceCategory: %ls", pwzId);
248
249 er = ::MsiGetComponentStateW(hInstall, pwzComponent, &isInstalled, &isAction);
250 hr = HRESULT_FROM_WIN32(er);
251 ExitOnFailure(hr, "Failed to get Component state for Wix4PerformanceCategory: %ls", pwzId);
252
253 if ((fInstall && !WcaIsInstalling(isInstalled, isAction)) ||
254 (!fInstall && !WcaIsUninstalling(isInstalled, isAction)))
255 {
256 continue;
257 }
258
259 hr = WcaGetRecordString(hRec, pcdqName, &pwzName);
260 ExitOnFailure(hr, "Failed to get Name for Wix4PerformanceCategory: %ls", pwzId);
261 hr = WcaWriteStringToCaData(pwzName, &pwzCustomActionData);
262 ExitOnFailure(hr, "Failed to add Name to CustomActionData for Wix4PerformanceCategory: %ls", pwzId);
263
264 hr = WcaGetRecordString(hRec, pcdqIniData, &pwzData);
265 ExitOnFailure(hr, "Failed to get IniData for Wix4PerformanceCategory: %ls", pwzId);
266 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
267 ExitOnFailure(hr, "Failed to add IniData to CustomActionData for Wix4PerformanceCategory: %ls", pwzId);
268
269 hr = WcaGetRecordString(hRec, pcdqConstantData, &pwzData);
270 ExitOnFailure(hr, "Failed to get ConstantData for Wix4PerformanceCategory: %ls", pwzId);
271 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
272 ExitOnFailure(hr, "Failed to add ConstantData to CustomActionData for Wix4PerformanceCategory: %ls", pwzId);
273 }
274
275 if (hr == E_NOMOREITEMS)
276 {
277 hr = S_OK;
278 }
279 ExitOnFailure(hr, "Failure while processing Wix4PerformanceCategory table.");
280
281 // If there was any data built up, schedule it for execution.
282 if (pwzCustomActionData)
283 {
284 if (fInstall)
285 {
286 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackRegisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_UNREGISTER);
287 ExitOnFailure(hr, "Failed to schedule RollbackRegisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
288
289 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RegisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_REGISTER);
290 ExitOnFailure(hr, "Failed to schedule RegisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
291 }
292 else
293 {
294 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackUnregisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_REGISTER);
295 ExitOnFailure(hr, "Failed to schedule RollbackUnregisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
296
297 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"UnregisterPerfCounterData"), pwzCustomActionData, COST_PERFMON_UNREGISTER);
298 ExitOnFailure(hr, "Failed to schedule UnregisterPerfCounterData action for Wix4PerformanceCategory: %ls", pwzId);
299 }
300 }
301
302LExit:
303 ReleaseStr(pwzCustomActionData);
304 ReleaseStr(pwzData);
305 ReleaseStr(pwzName);
306 ReleaseStr(pwzComponent);
307 ReleaseStr(pwzId);
308
309 return hr;
310}
diff --git a/src/ext/Util/ca/scaperfexec.cpp b/src/ext/Util/ca/scaperfexec.cpp
new file mode 100644
index 00000000..c5425754
--- /dev/null
+++ b/src/ext/Util/ca/scaperfexec.cpp
@@ -0,0 +1,423 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5typedef DWORD (STDAPICALLTYPE *PFNPERFCOUNTERTEXTSTRINGS)(LPWSTR lpCommandLine, BOOL bQuietModeArg);
6
7static HRESULT ExecutePerfCounterData(
8 __in MSIHANDLE hInstall,
9 __in BOOL fInstall
10 );
11static HRESULT CreateDataFile(
12 __in LPCWSTR wzTempFolder,
13 __in LPCWSTR wzData,
14 __in BOOL fIniData,
15 __out HANDLE *phFile,
16 __out_opt LPWSTR *ppwzFile
17 );
18
19
20/********************************************************************
21 RegisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering
22 performance counters
23
24 Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t...
25*******************************************************************/
26extern "C" UINT __stdcall RegisterPerfCounterData(
27 __in MSIHANDLE hInstall
28 )
29{
30 // AssertSz(FALSE, "debug RegisterPerfCounterData()");
31 HRESULT hr = S_OK;
32 DWORD er = ERROR_SUCCESS;
33
34 hr = WcaInitialize(hInstall, "RegisterPerfCounterData");
35 ExitOnFailure(hr, "Failed to initialize RegisterPerfCounterData.");
36
37 hr = ExecutePerfCounterData(hInstall, TRUE);
38 MessageExitOnFailure(hr, msierrInstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table.");
39
40LExit:
41 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
42 return WcaFinalize(er);
43}
44
45
46/********************************************************************
47 UnregisterPerfCounterData - CUSTOM ACTION ENTRY POINT for registering
48 performance counters
49
50 Input: deferred CustomActionData: wzName\twzIniData\twzConstantData\twzName\twzIniData\twzConstantData\t...
51*******************************************************************/
52extern "C" UINT __stdcall UnregisterPerfCounterData(
53 __in MSIHANDLE hInstall
54 )
55{
56 // AssertSz(FALSE, "debug UnregisterPerfCounterData()");
57 HRESULT hr = S_OK;
58 DWORD er = ERROR_SUCCESS;
59
60 hr = WcaInitialize(hInstall, "UnregisterPerfCounterData");
61 ExitOnFailure(hr, "Failed to initialize UnregisterPerfCounterData.");
62
63 hr = ExecutePerfCounterData(hInstall, FALSE);
64 MessageExitOnFailure(hr, msierrUninstallPerfCounterData, "Failed to execute Wix4PerformanceCategory table.");
65
66LExit:
67 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
68 return WcaFinalize(er);
69}
70
71
72/********************************************************************
73 RegisterPerfmon - CUSTOM ACTION ENTRY POINT for registering
74 counters
75
76 Input: deferred CustomActionData -
77 wzFile or wzName
78*******************************************************************/
79extern "C" UINT __stdcall RegisterPerfmon(
80 __in MSIHANDLE hInstall
81 )
82{
83// Assert(FALSE);
84 UINT er = ERROR_SUCCESS;
85 HRESULT hr = S_OK;
86 LPWSTR pwzData = NULL;
87
88 HMODULE hMod = NULL;
89 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString;
90 DWORD dwRet;
91 LPWSTR pwzShortPath = NULL;
92 DWORD cchShortPath = MAX_PATH;
93 DWORD cchShortPathLength = 0;
94
95 LPWSTR pwzCommand = NULL;
96
97 hr = WcaInitialize(hInstall, "RegisterPerfmon");
98 ExitOnFailure(hr, "failed to initialize");
99
100 hr = WcaGetProperty(L"CustomActionData", &pwzData);
101 ExitOnFailure(hr, "failed to get CustomActionData");
102
103 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
104
105 // do the perfmon registration
106 if (NULL == hMod)
107 {
108 hr = LoadSystemLibrary(L"loadperf.dll", &hMod);
109 }
110 ExitOnFailure(hr, "failed to load DLL for PerfMon");
111
112 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "LoadPerfCounterTextStringsW");
113 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon");
114
115 hr = StrAlloc(&pwzShortPath, cchShortPath);
116 ExitOnFailure(hr, "failed to allocate string");
117
118 WcaLog(LOGMSG_VERBOSE, "Converting DLL path to short format: %ls", pwzData);
119 cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath);
120 if (cchShortPathLength > cchShortPath)
121 {
122 cchShortPath = cchShortPathLength + 1;
123 hr = StrAlloc(&pwzShortPath, cchShortPath);
124 ExitOnFailure(hr, "failed to allocate string");
125
126 cchShortPathLength = ::GetShortPathNameW(pwzData, pwzShortPath, cchShortPath);
127 }
128
129 if (0 == cchShortPathLength)
130 {
131 ExitOnLastError(hr, "failed to get short path format of path: %ls", pwzData);
132 }
133
134 hr = StrAllocFormatted(&pwzCommand, L"lodctr \"%s\"", pwzShortPath);
135 ExitOnFailure(hr, "failed to format lodctr string");
136
137 WcaLog(LOGMSG_VERBOSE, "RegisterPerfmon running command: '%ls'", pwzCommand);
138 dwRet = (*pfnPerfCounterTextString)(pwzCommand, TRUE);
139 if (dwRet != ERROR_SUCCESS && dwRet != ERROR_ALREADY_EXISTS)
140 {
141 hr = HRESULT_FROM_WIN32(dwRet);
142 MessageExitOnFailure(hr, msierrPERFMONFailedRegisterDLL, "failed to register with PerfMon, DLL: %ls", pwzData);
143 }
144
145 hr = S_OK;
146LExit:
147 ReleaseStr(pwzData);
148
149 if (FAILED(hr))
150 er = ERROR_INSTALL_FAILURE;
151 return WcaFinalize(er);
152}
153
154
155extern "C" UINT __stdcall UnregisterPerfmon(
156 __in MSIHANDLE hInstall
157 )
158{
159// Assert(FALSE);
160 UINT er = ERROR_SUCCESS;
161 HRESULT hr = S_OK;
162 LPWSTR pwzData = NULL;
163
164 HMODULE hMod = NULL;
165 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString;
166 DWORD dwRet;
167 WCHAR wz[255];
168
169 hr = WcaInitialize(hInstall, "UnregisterPerfmon");
170 ExitOnFailure(hr, "failed to initialize");
171
172 hr = WcaGetProperty(L"CustomActionData", &pwzData);
173 ExitOnFailure(hr, "failed to get CustomActionData");
174
175 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
176
177 // do the perfmon unregistration
178 hr = E_FAIL;
179 if (hMod == NULL)
180 {
181 hr = LoadSystemLibrary(L"loadperf.dll", &hMod);
182 }
183 ExitOnFailure(hr, "failed to load DLL for PerfMon");
184
185 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hMod, "UnloadPerfCounterTextStringsW");
186 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "failed to get DLL function for PerfMon");
187
188 hr = ::StringCchPrintfW(wz, countof(wz), L"unlodctr \"%s\"", pwzData);
189 ExitOnFailure(hr, "Failed to format unlodctr string with: %ls", pwzData);
190 WcaLog(LOGMSG_VERBOSE, "UnregisterPerfmon running command: '%ls'", wz);
191 dwRet = (*pfnPerfCounterTextString)(wz, TRUE);
192 // if the counters aren't registered, then OK to continue
193 if (dwRet != ERROR_SUCCESS && dwRet != ERROR_FILE_NOT_FOUND && dwRet != ERROR_BADKEY)
194 {
195 hr = HRESULT_FROM_WIN32(dwRet);
196 MessageExitOnFailure(hr, msierrPERFMONFailedUnregisterDLL, "failed to unregsister with PerfMon, DLL: %ls", pwzData);
197 }
198
199 hr = S_OK;
200LExit:
201 ReleaseStr(pwzData);
202
203 if (FAILED(hr))
204 er = ERROR_INSTALL_FAILURE;
205 return WcaFinalize(er);
206}
207
208
209static HRESULT ExecutePerfCounterData(
210 __in MSIHANDLE /*hInstall*/,
211 __in BOOL fInstall
212 )
213{
214 HRESULT hr = S_OK;
215 DWORD er = ERROR_SUCCESS;
216
217 HMODULE hModule = NULL;
218 PFNPERFCOUNTERTEXTSTRINGS pfnPerfCounterTextString = NULL;
219 LPCWSTR wzPrefix = NULL;
220
221 LPWSTR pwzCustomActionData = NULL;
222 LPWSTR pwz = NULL;
223
224 LPWSTR pwzName = NULL;
225 LPWSTR pwzIniData = NULL;
226 LPWSTR pwzConstantData = NULL;
227 LPWSTR pwzTempFolder = NULL;
228 LPWSTR pwzIniFile = NULL;
229 LPWSTR pwzExecute = NULL;
230
231 HANDLE hIniData = INVALID_HANDLE_VALUE;
232 HANDLE hConstantData = INVALID_HANDLE_VALUE;
233
234 // Load the system performance counter helper DLL then get the appropriate
235 // entrypoint out of it. Fortunately, they have the same signature so we
236 // can use one function pointer to point to both.
237 hr = LoadSystemLibrary(L"loadperf.dll", &hModule);
238 ExitOnFailure(hr, "failed to load DLL for PerfMon");
239
240 if (fInstall)
241 {
242 wzPrefix = L"lodctr";
243 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "LoadPerfCounterTextStringsW");
244 }
245 else
246 {
247 wzPrefix = L"unlodctr";
248 pfnPerfCounterTextString = (PFNPERFCOUNTERTEXTSTRINGS)::GetProcAddress(hModule, "UnloadPerfCounterTextStringsW");
249 }
250 ExitOnNullWithLastError(pfnPerfCounterTextString, hr, "Failed to get DLL function for PerfMon");
251
252 // Now get the CustomActionData and execute it.
253 hr = WcaGetProperty(L"CustomActionData", &pwzCustomActionData);
254 ExitOnFailure(hr, "Failed to get CustomActionData.");
255
256 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
257
258 pwz = pwzCustomActionData;
259
260 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzName)))
261 {
262 hr = WcaReadStringFromCaData(&pwz, &pwzIniData);
263 ExitOnFailure(hr, "Failed to read IniData from custom action data.");
264
265 hr = WcaReadStringFromCaData(&pwz, &pwzConstantData);
266 ExitOnFailure(hr, "Failed to read ConstantData from custom action data.");
267
268 if (fInstall)
269 {
270 hr = PathCreateTempDirectory(NULL, L"WIXPF%03x", 999, &pwzTempFolder);
271 ExitOnFailure(hr, "Failed to create temp directory.");
272
273 hr = CreateDataFile(pwzTempFolder, pwzIniData, TRUE, &hIniData, &pwzIniFile);
274 ExitOnFailure(hr, "Failed to create .ini file for performance counter category: %ls", pwzName);
275
276 hr = CreateDataFile(pwzTempFolder, pwzConstantData, FALSE, &hConstantData, NULL);
277 ExitOnFailure(hr, "Failed to create .h file for performance counter category: %ls", pwzName);
278
279 hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzIniFile);
280 ExitOnFailure(hr, "Failed to allocate string to execute.");
281
282 // Execute the install.
283 er = (*pfnPerfCounterTextString)(pwzExecute, TRUE);
284 hr = HRESULT_FROM_WIN32(er);
285 ExitOnFailure(hr, "Failed to execute install of performance counter category: %ls", pwzName);
286
287 if (INVALID_HANDLE_VALUE != hIniData)
288 {
289 ::CloseHandle(hIniData);
290 hIniData = INVALID_HANDLE_VALUE;
291 }
292
293 if (INVALID_HANDLE_VALUE != hConstantData)
294 {
295 ::CloseHandle(hConstantData);
296 hConstantData = INVALID_HANDLE_VALUE;
297 }
298
299 DirEnsureDelete(pwzTempFolder, TRUE, TRUE);
300 }
301 else
302 {
303 hr = StrAllocFormatted(&pwzExecute, L"%s \"%s\"", wzPrefix, pwzName);
304 ExitOnFailure(hr, "Failed to allocate string to execute.");
305
306 // Execute the uninstall and if the counter isn't registered then ignore
307 // the error since it won't hurt anything.
308 er = (*pfnPerfCounterTextString)(pwzExecute, TRUE);
309 if (ERROR_FILE_NOT_FOUND == er || ERROR_BADKEY == er)
310 {
311 er = ERROR_SUCCESS;
312 }
313 hr = HRESULT_FROM_WIN32(er);
314 ExitOnFailure(hr, "Failed to execute uninstall of performance counter category: %ls", pwzName);
315 }
316 }
317
318 if (E_NOMOREITEMS == hr) // If there are no more items, all is well
319 {
320 hr = S_OK;
321 }
322 ExitOnFailure(hr, "Failed to execute all perf counter data.");
323
324 hr = S_OK;
325
326LExit:
327 if (INVALID_HANDLE_VALUE != hIniData)
328 {
329 ::CloseHandle(hIniData);
330 }
331
332 if (INVALID_HANDLE_VALUE != hConstantData)
333 {
334 ::CloseHandle(hConstantData);
335 }
336
337 ReleaseStr(pwzExecute);
338 ReleaseStr(pwzIniFile);
339 ReleaseStr(pwzTempFolder);
340 ReleaseStr(pwzConstantData);
341 ReleaseStr(pwzIniData);
342 ReleaseStr(pwzName);
343 ReleaseStr(pwzCustomActionData);
344
345 if (hModule)
346 {
347 ::FreeLibrary(hModule);
348 }
349
350 return hr;
351}
352
353
354static HRESULT CreateDataFile(
355 __in LPCWSTR wzTempFolder,
356 __in LPCWSTR wzData,
357 __in BOOL fIniData,
358 __out HANDLE *phFile,
359 __out_opt LPWSTR *ppwzFile
360 )
361{
362 HRESULT hr = S_OK;
363 HANDLE hFile = INVALID_HANDLE_VALUE;
364 LPWSTR pwzFile = NULL;
365 LPSTR pszData = NULL;
366 DWORD cbData = 0;
367 DWORD cbWritten = 0;
368
369 // Convert the data to UTF-8 because lodctr/unloctr
370 // doesn't like unicode.
371 hr = StrAnsiAllocString(&pszData, wzData, 0, CP_UTF8);
372 ExitOnFailure(hr, "Failed to covert data to ANSI.");
373
374 cbData = lstrlenA(pszData);
375
376 // Concatenate the paths together, open the file data file
377 // and dump the data in there.
378 hr = StrAllocString(&pwzFile, wzTempFolder, 0);
379 ExitOnFailure(hr, "Failed to copy temp directory name.");
380
381 hr = StrAllocConcat(&pwzFile, L"wixperf", 0);
382 ExitOnFailure(hr, "Failed to add name of file.");
383
384 hr = StrAllocConcat(&pwzFile, fIniData ? L".ini" : L".h", 0);
385 ExitOnFailure(hr, "Failed to add extension of file.");
386
387 hFile = ::CreateFileW(pwzFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
388 if (INVALID_HANDLE_VALUE == hFile)
389 {
390 ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFile);
391 }
392
393 if (!::WriteFile(hFile, pszData, cbData, &cbWritten, NULL))
394 {
395 ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFile);
396 }
397
398 if (INVALID_HANDLE_VALUE != hFile)
399 {
400 ::CloseHandle(hFile);
401 hFile = INVALID_HANDLE_VALUE;
402 }
403
404 // Return the requested values.
405 *phFile = hFile;
406 hFile = INVALID_HANDLE_VALUE;
407
408 if (ppwzFile)
409 {
410 *ppwzFile = pwzFile;
411 pwzFile = NULL;
412 }
413
414LExit:
415 if (INVALID_HANDLE_VALUE != hFile)
416 {
417 ::CloseHandle(hFile);
418 }
419 ReleaseStr(pszData);
420 ReleaseStr(pwzFile);
421
422 return hr;
423}
diff --git a/src/ext/Util/ca/scasched.cpp b/src/ext/Util/ca/scasched.cpp
new file mode 100644
index 00000000..d81b1f14
--- /dev/null
+++ b/src/ext/Util/ca/scasched.cpp
@@ -0,0 +1,127 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/********************************************************************
7ConfigureSmb - CUSTOM ACTION ENTRY POINT for installing fileshare settings
8
9********************************************************************/
10extern "C" UINT __stdcall ConfigureSmbInstall(
11 __in MSIHANDLE hInstall
12 )
13{
14 HRESULT hr = S_OK;
15 UINT er = ERROR_SUCCESS;
16
17 SCA_SMB* pssList = NULL;
18
19 // initialize
20 hr = WcaInitialize(hInstall, "ConfigureSmbInstall");
21 ExitOnFailure(hr, "Failed to initialize");
22
23 // check to see if necessary tables are specified
24 if (WcaTableExists(L"Wix4FileShare") != S_OK)
25 {
26 WcaLog(LOGMSG_VERBOSE, "Skipping SMB CustomAction, no Wix4FileShare table");
27 ExitFunction1(hr = S_FALSE);
28 }
29
30 hr = ScaSmbRead(&pssList);
31 ExitOnFailure(hr, "failed to read Wix4FileShare table");
32
33 hr = ScaSmbInstall(pssList);
34 ExitOnFailure(hr, "failed to install FileShares");
35
36LExit:
37 if (pssList)
38 ScaSmbFreeList(pssList);
39
40 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
41 return WcaFinalize(er);
42}
43
44
45/********************************************************************
46ConfigureSmb - CUSTOM ACTION ENTRY POINT for uninstalling fileshare settings
47
48********************************************************************/
49extern "C" UINT __stdcall ConfigureSmbUninstall(
50 __in MSIHANDLE hInstall
51 )
52{
53 HRESULT hr = S_OK;
54 UINT er = ERROR_SUCCESS;
55
56 SCA_SMB* pssList = NULL;
57
58 // initialize
59 hr = WcaInitialize(hInstall, "ConfigureSmbUninstall");
60 ExitOnFailure(hr, "Failed to initialize");
61
62 // check to see if necessary tables are specified
63 if (WcaTableExists(L"Wix4FileShare") != S_OK)
64 {
65 WcaLog(LOGMSG_VERBOSE, "Skipping SMB CustomAction, no Wix4FileShare table");
66 ExitFunction1(hr = S_FALSE);
67 }
68
69 hr = ScaSmbRead(&pssList);
70 ExitOnFailure(hr, "failed to read Wix4FileShare table");
71
72 hr = ScaSmbUninstall(pssList);
73 ExitOnFailure(hr, "failed to uninstall FileShares");
74
75LExit:
76 if (pssList)
77 ScaSmbFreeList(pssList);
78
79 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
80 return WcaFinalize(er);
81}
82
83
84/********************************************************************
85ConfigureUsers - CUSTOM ACTION ENTRY POINT for installing users
86
87********************************************************************/
88extern "C" UINT __stdcall ConfigureUsers(
89 __in MSIHANDLE hInstall
90 )
91{
92 //AssertSz(0, "Debug ConfigureUsers");
93
94 HRESULT hr = S_OK;
95 UINT er = ERROR_SUCCESS;
96
97 BOOL fInitializedCom = FALSE;
98 SCA_USER* psuList = NULL;
99
100 // initialize
101 hr = WcaInitialize(hInstall, "ConfigureUsers");
102 ExitOnFailure(hr, "Failed to initialize");
103
104 hr = ::CoInitialize(NULL);
105 ExitOnFailure(hr, "failed to initialize COM");
106 fInitializedCom = TRUE;
107
108 hr = ScaUserRead(&psuList);
109 ExitOnFailure(hr, "failed to read Wix4User table");
110
111 hr = ScaUserExecute(psuList);
112 ExitOnFailure(hr, "failed to add/remove User actions");
113
114LExit:
115 if (psuList)
116 {
117 ScaUserFreeList(psuList);
118 }
119
120 if (fInitializedCom)
121 {
122 ::CoUninitialize();
123 }
124
125 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
126 return WcaFinalize(er);
127} \ No newline at end of file
diff --git a/src/ext/Util/ca/scasmb.h b/src/ext/Util/ca/scasmb.h
new file mode 100644
index 00000000..f2a4b53c
--- /dev/null
+++ b/src/ext/Util/ca/scasmb.h
@@ -0,0 +1,46 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5#include "scauser.h"
6
7// structs
8// Structure used to hold and extra user/permission pairs from the Wix4FileSharePermissions Table
9struct SCA_SMB_EX_USER_PERMS
10{
11 int nPermissions;
12 ACCESS_MODE accessMode;
13 SCA_USER scau;
14 SCA_SMB_EX_USER_PERMS* pExUserPermsNext;
15};
16
17struct SCA_SMB // hungarian ss
18{
19 WCHAR wzId[MAX_DARWIN_KEY + 1];
20 WCHAR wzShareName[MAX_DARWIN_KEY + 1];
21 WCHAR wzDescription[MAX_DARWIN_COLUMN + 1];
22 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
23 WCHAR wzDirectory[MAX_PATH + 1];
24
25 int nUserPermissionCount;
26 int nPermissions;
27 SCA_SMB_EX_USER_PERMS* pExUserPerms;
28
29 INSTALLSTATE isInstalled, isAction;
30
31 BOOL fUseIntegratedAuth;
32 BOOL fLegacyUserProvided;
33 struct SCA_USER scau;
34
35 struct SCA_SMB* pssNext;
36};
37
38
39#define RESERVED 0
40
41// schedule prototypes
42HRESULT ScaSmbRead(SCA_SMB** ppssList);
43HRESULT ScaSmbExPermsRead(SCA_SMB* pss);
44HRESULT ScaSmbUninstall(SCA_SMB* pssList);
45HRESULT ScaSmbInstall(SCA_SMB* pssList);
46void ScaSmbFreeList(SCA_SMB* pssList);
diff --git a/src/ext/Util/ca/scasmbexec.cpp b/src/ext/Util/ca/scasmbexec.cpp
new file mode 100644
index 00000000..ced3aa78
--- /dev/null
+++ b/src/ext/Util/ca/scasmbexec.cpp
@@ -0,0 +1,316 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/********************************************************************
7 AllocateAcl - allocate an acl and populate it with this user and
8 permission information user could be user or domain\user
9
10********************************************************************/
11HRESULT AllocateAcl(SCA_SMBP* pssp, PACL* ppACL)
12{
13 HRESULT hr = S_OK;
14 EXPLICIT_ACCESSW* pEA = NULL;
15 DWORD cEA = 0;
16 DWORD dwCounter = 0;
17
18 PSID psid = NULL;
19 LPCWSTR wzUser = NULL;
20 DWORD nPermissions = 0;
21 DWORD nErrorReturn = 0;
22 ACCESS_MODE accessMode = NOT_USED_ACCESS;
23
24 cEA = pssp->dwUserPermissionCount + 1;
25 if (cEA >= MAXSIZE_T / sizeof(EXPLICIT_ACCESSW))
26 {
27 ExitOnFailure(hr = E_OUTOFMEMORY, "Too many user permissions to allocate: %u", cEA);
28 }
29
30 pEA = static_cast<EXPLICIT_ACCESSW*>(MemAlloc(cEA * sizeof(EXPLICIT_ACCESSW), TRUE));
31 ExitOnNull(pEA, hr, E_OUTOFMEMORY, "failed to allocate memory for explicit access structure");
32
33 // figure out how big the psid is
34 for (dwCounter = 0; dwCounter < pssp->dwUserPermissionCount; ++dwCounter)
35 {
36 wzUser = pssp->pUserPerms[dwCounter].wzUser;
37 nPermissions = pssp->pUserPerms[dwCounter].nPermissions;
38 accessMode = pssp->pUserPerms[dwCounter].accessMode;
39 //
40 // create the appropriate SID
41 //
42
43 // figure out the right user to put into the access block
44 if (0 == lstrcmpW(wzUser, L"Everyone"))
45 {
46 hr = AclGetWellKnownSid(WinWorldSid, &psid);
47 }
48 else if (0 == lstrcmpW(wzUser, L"Administrators"))
49 {
50 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
51 }
52 else if (0 == lstrcmpW(wzUser, L"LocalSystem"))
53 {
54 hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
55 }
56 else if (0 == lstrcmpW(wzUser, L"LocalService"))
57 {
58 hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
59 }
60 else if (0 == lstrcmpW(wzUser, L"NetworkService"))
61 {
62 hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
63 }
64 else if (0 == lstrcmpW(wzUser, L"AuthenticatedUser"))
65 {
66 hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
67 }
68 else if (0 == lstrcmpW(wzUser, L"Guests"))
69 {
70 hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
71 }
72 else if(0 == lstrcmpW(wzUser, L"CREATOR OWNER"))
73 {
74 hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
75 }
76 else
77 {
78 hr = AclGetAccountSid(NULL, wzUser, &psid);
79 }
80 ExitOnFailure(hr, "failed to get sid for account: %ls", wzUser);
81
82 // we now have a valid pSid, fill in the EXPLICIT_ACCESS
83
84 /* Permissions options: (see sca.sdh for defined sdl options)
85 #define GENERIC_READ (0x80000000L) 2147483648
86 #define GENERIC_WRITE (0x40000000L) 1073741824
87 #define GENERIC_EXECUTE (0x20000000L) 536870912
88 #define GENERIC_ALL (0x10000000L) 268435456
89 */
90 pEA[dwCounter].grfAccessPermissions = nPermissions;
91 pEA[dwCounter].grfAccessMode = accessMode;
92 pEA[dwCounter].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
93#pragma prefast(push)
94#pragma prefast(disable:25029)
95 ::BuildTrusteeWithSidW(&(pEA[dwCounter].Trustee), psid);
96#pragma prefast(pop)
97 }
98
99 // create a new ACL that contains the ACE
100 *ppACL = NULL;
101#pragma prefast(push)
102#pragma prefast(disable:25029)
103 nErrorReturn = ::SetEntriesInAclW(dwCounter, pEA, NULL, ppACL);
104#pragma prefast(pop)
105 ExitOnFailure(hr = HRESULT_FROM_WIN32(nErrorReturn), "failed to allocate ACL");
106
107LExit:
108 if (psid)
109 {
110 AclFreeSid(psid);
111 }
112
113 ReleaseMem(pEA);
114
115 return hr;
116}
117
118
119
120/********************************************************************
121 FillShareInfo - fill the NetShareAdd data structure
122
123********************************************************************/
124void FillShareInfo(SHARE_INFO_502* psi, SCA_SMBP* pssp, PSECURITY_DESCRIPTOR pSD)
125{
126 psi->shi502_netname = pssp->wzKey;
127 psi->shi502_type = STYPE_DISKTREE;
128 psi->shi502_remark = pssp->wzDescription;
129 psi->shi502_permissions = 0; // not used
130 psi->shi502_max_uses = 0xFFFFFFFF;
131 psi->shi502_current_uses = 0;
132 psi->shi502_path = pssp->wzDirectory;
133 psi->shi502_passwd = NULL; // not file share perms
134 psi->shi502_reserved = 0;
135 psi->shi502_security_descriptor = pSD;
136}
137
138
139
140/* NET_API_STATUS return codes
141NERR_Success = 0
142NERR_DuplicateShare = 2118
143NERR_BufTooSmall = 2123
144NERR_NetNameNotFound = 2310
145NERR_RedirectedPath = 2117
146NERR_UnknownDevDir = 2116
147*/
148
149/********************************************************************
150 DoesShareExists - Does a share of this name exist on this computer?
151
152********************************************************************/
153HRESULT DoesShareExist(__in LPWSTR wzShareName)
154{
155 HRESULT hr = S_OK;
156 NET_API_STATUS s;
157 SHARE_INFO_502* psi = NULL;
158 s = ::NetShareGetInfo(NULL, wzShareName, 502, (BYTE**) &psi);
159
160 switch (s)
161 {
162 case NERR_Success:
163 hr = S_OK;
164 break;
165 case NERR_NetNameNotFound:
166 hr = E_FILENOTFOUND;
167 break;
168 default:
169 WcaLogError(s, "NetShareGetInfo returned an unexpected value.", NULL);
170 hr = HRESULT_FROM_WIN32(s);
171 break;
172 }
173
174 ::NetApiBufferFree(psi);
175
176 return hr;
177}
178
179
180
181/********************************************************************
182 CreateShare - create the file share on this computer
183
184********************************************************************/
185HRESULT CreateShare(SCA_SMBP* pssp)
186{
187 if (!pssp || !(pssp->wzKey))
188 return E_INVALIDARG;
189
190 HRESULT hr = S_OK;
191 PACL pACL = NULL;
192 SHARE_INFO_502 si;
193 NET_API_STATUS s;
194 DWORD dwParamErr = 0;
195
196 BOOL fShareExists = SUCCEEDED(DoesShareExist(pssp->wzKey));
197
198 PSECURITY_DESCRIPTOR pSD = static_cast<PSECURITY_DESCRIPTOR>(MemAlloc(SECURITY_DESCRIPTOR_MIN_LENGTH, TRUE));
199 ExitOnNull(pSD, hr, E_OUTOFMEMORY, "Failed to allocate memory for security descriptor");
200
201#pragma prefast(push)
202#pragma prefast(disable:25029)
203 if (!::InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
204#pragma prefast(pop)
205 {
206 ExitOnLastError(hr, "failed to initialize security descriptor");
207 }
208
209 hr = AllocateAcl(pssp, &pACL);
210 ExitOnFailure(hr, "Failed to allocate ACL for fileshare");
211
212 if (NULL == pACL)
213 {
214 WcaLog(LOGMSG_VERBOSE, "Ignoring NULL DACL.");
215 }
216#pragma prefast(push)
217#pragma prefast(disable:25028) // We only call this when pACL isn't NULL, so this call is safe according to the docs
218 // add the ACL to the security descriptor.
219 else if (!::SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE))
220 {
221 ExitOnLastError(hr, "Failed to set security descriptor");
222 }
223#pragma prefast(pop)
224
225 // all that is left is to create the share
226 FillShareInfo(&si, pssp, pSD);
227
228 // Fail if the directory doesn't exist
229 if (!DirExists(pssp->wzDirectory, NULL))
230 ExitOnFailure(hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND), "Can't create a file share on directory that doesn't exist: %ls.", pssp->wzDirectory);
231
232 WcaLog(LOGMSG_VERBOSE, "Creating file share on directory \'%ls\' named \'%ls\'.", pssp->wzDirectory, pssp->wzKey);
233
234 if (!fShareExists)
235 {
236 s = ::NetShareAdd(NULL, 502, (BYTE*) &si, &dwParamErr);
237 WcaLog(LOGMSG_VERBOSE, "Adding a new file share.");
238 }
239 else
240 {
241 // The share exists. Write our new permissions over the top.
242 s = ::NetShareSetInfo(NULL, pssp->wzKey, 502, (BYTE*) &si, &dwParamErr);
243 WcaLog(LOGMSG_VERBOSE, "Setting permissions on existing share.");
244 }
245
246 if (NERR_Success != s)
247 {
248 hr = E_FAIL;
249 if (!fShareExists && NERR_DuplicateShare == s)
250 WcaLog(LOGMSG_VERBOSE, "Duplicate error when existence check failed.");
251
252 // error codes listed above.
253 ExitOnFailure(hr, "Failed to create/modify file share: Err: %d", s);
254 }
255
256LExit:
257 if (pACL)
258 {
259 ::LocalFree(pACL);
260 }
261
262 ReleaseMem(pSD);
263
264 return hr;
265}
266
267
268/********************************************************************
269 ScaEnsureSmbExists
270
271********************************************************************/
272HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp)
273{
274 HRESULT hr = S_OK;
275
276 // create the share
277 hr = CreateShare(pssp);
278
279 return hr;
280}
281
282
283//
284// Delete File Shares - real work
285//
286
287/********************************************************************
288 ScaDropSmb - delete this file share from this computer
289
290********************************************************************/
291HRESULT ScaDropSmb(SCA_SMBP* pssp)
292{
293 HRESULT hr = S_OK;
294 NET_API_STATUS s;
295
296 hr = DoesShareExist(pssp->wzKey);
297
298 if (E_FILENOTFOUND == hr)
299 {
300 WcaLog(LOGMSG_VERBOSE, "Share doesn't exist, share removal skipped. (%ls)", pssp->wzKey);
301 ExitFunction1(hr = S_OK);
302
303 }
304
305 ExitOnFailure(hr, "Unable to detect share. (%ls)", pssp->wzKey);
306
307 s = ::NetShareDel(NULL, pssp->wzKey, 0);
308 if (NERR_Success != s)
309 {
310 hr = E_FAIL;
311 ExitOnFailure(hr, "Failed to remove file share: Err: %d", s);
312 }
313
314LExit:
315 return hr;
316}
diff --git a/src/ext/Util/ca/scasmbexec.h b/src/ext/Util/ca/scasmbexec.h
new file mode 100644
index 00000000..e3c8f8bb
--- /dev/null
+++ b/src/ext/Util/ca/scasmbexec.h
@@ -0,0 +1,27 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5struct SCA_SMBP_USER_PERMS
6{
7 DWORD nPermissions;
8 ACCESS_MODE accessMode;
9 WCHAR* wzUser;
10 //Not adding Password because I can't find anywhere that it is used
11};
12
13struct SCA_SMBP // hungarian ssp
14{
15 WCHAR* wzKey;
16 WCHAR* wzDescription;
17 WCHAR* wzComponent;
18 WCHAR* wzDirectory; // full path of the dir to share to
19
20 DWORD dwUserPermissionCount; //Count of SCA_SMBP_EX_USER_PERMS structures
21 SCA_SMBP_USER_PERMS* pUserPerms;
22 BOOL fUseIntegratedAuth;
23};
24
25
26HRESULT ScaEnsureSmbExists(SCA_SMBP* pssp);
27HRESULT ScaDropSmb(SCA_SMBP* pssp);
diff --git a/src/ext/Util/ca/scasmbsched.cpp b/src/ext/Util/ca/scasmbsched.cpp
new file mode 100644
index 00000000..e29f7f51
--- /dev/null
+++ b/src/ext/Util/ca/scasmbsched.cpp
@@ -0,0 +1,639 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5
6/********************************************************************
7 Helper functions to maintain a list of file shares to create / remove
8
9********************************************************************/
10SCA_SMB* NewSmb()
11{
12 SCA_SMB* pss = (SCA_SMB*)MemAlloc(sizeof(SCA_SMB), TRUE);
13 Assert(pss);
14 return pss;
15}
16
17
18SCA_SMB_EX_USER_PERMS* NewExUserPermsSmb()
19{
20 SCA_SMB_EX_USER_PERMS* pExUserPerms = (SCA_SMB_EX_USER_PERMS*)MemAlloc(sizeof(SCA_SMB_EX_USER_PERMS), TRUE);
21 Assert(pExUserPerms);
22 return pExUserPerms;
23}
24
25
26SCA_SMB* AddSmbToList(SCA_SMB* pssList, SCA_SMB* pss)
27{
28 if (pssList)
29 {
30 SCA_SMB* pssT = pssList;
31 while (pssT->pssNext)
32 {
33 pssT = pssT->pssNext;
34 }
35
36 pssT->pssNext = pss;
37 }
38 else
39 {
40 pssList = pss;
41 }
42
43 return pssList;
44}
45
46
47SCA_SMB_EX_USER_PERMS* AddExUserPermsSmbToList(
48 SCA_SMB_EX_USER_PERMS* pExUserPermsList,
49 SCA_SMB_EX_USER_PERMS* pExUserPerms
50 )
51{
52 SCA_SMB_EX_USER_PERMS* pExUserPermsTemp = pExUserPermsList;
53 if (pExUserPermsList)
54 {
55 while (pExUserPermsTemp->pExUserPermsNext)
56 {
57 pExUserPermsTemp = pExUserPermsTemp->pExUserPermsNext;
58 }
59
60 pExUserPermsTemp->pExUserPermsNext = pExUserPerms;
61 }
62 else
63 {
64 pExUserPermsList = pExUserPerms;
65 }
66
67 return pExUserPermsList;
68}
69
70void ScaSmbFreeList(SCA_SMB* pssList)
71{
72 SCA_SMB* pssDelete = pssList;
73 while (pssList)
74 {
75 pssDelete = pssList;
76 pssList = pssList->pssNext;
77
78 MemFree(pssDelete);
79 }
80}
81
82void ScaExUserPermsSmbFreeList(SCA_SMB_EX_USER_PERMS* pExUserPermsList)
83{
84 SCA_SMB_EX_USER_PERMS* pExUserPermsDelete = pExUserPermsList;
85 while (pExUserPermsList)
86 {
87 pExUserPermsDelete = pExUserPermsList;
88 pExUserPermsList = pExUserPermsList->pExUserPermsNext;
89
90 MemFree(pExUserPermsDelete);
91 }
92}
93
94// sql query constants
95LPCWSTR vcsSmbQuery = L"SELECT `Wix4FileShare`, `ShareName`, `Description`, `Directory_`, "
96 L"`Component_`, `User_`, `Permissions` FROM `Wix4FileShare`";
97
98enum eSmbQuery {
99 ssqFileShare = 1,
100 ssqShareName,
101 ssqDescription,
102 ssqDirectory,
103 ssqComponent,
104 ssqUser,
105 ssqPermissions
106 };
107
108
109/********************************************************************
110 ScaSmbRead - read all of the information from the msi tables and
111 return a list of file share jobs to be done.
112
113********************************************************************/
114HRESULT ScaSmbRead(SCA_SMB** ppssList)
115{
116 HRESULT hr = S_OK;
117 UINT er = ERROR_SUCCESS;
118 PMSIHANDLE hView, hRec;
119
120 LPWSTR pwzData = NULL;
121
122 SCA_SMB* pss = NULL;
123 BOOL bUserPermissionsTableExists = FALSE;
124
125 if (S_OK != WcaTableExists(L"Wix4FileShare"))
126 {
127 WcaLog(LOGMSG_VERBOSE, "Skipping ScaSmbCreateShare() - Wix4FileShare table not present");
128 ExitFunction1(hr = S_FALSE);
129 }
130
131 if (S_OK == WcaTableExists(L"Wix4FileSharePermissions"))
132 {
133 bUserPermissionsTableExists = TRUE;
134 }
135 else
136 {
137 WcaLog(LOGMSG_VERBOSE, "No Additional Permissions - Wix4FileSharePermissions table not present");
138 }
139
140 WcaLog(LOGMSG_VERBOSE, "Reading File Share Tables");
141
142 // loop through all the fileshares
143 hr = WcaOpenExecuteView(vcsSmbQuery, &hView);
144 ExitOnFailure(hr, "Failed to open view on Wix4FileShare table");
145 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
146 {
147 pss = NewSmb();
148 if (!pss)
149 {
150 hr = E_OUTOFMEMORY;
151 break;
152 }
153 Assert(pss);
154 ::ZeroMemory(pss, sizeof(*pss));
155
156 hr = WcaGetRecordString(hRec, ssqFileShare, &pwzData);
157 ExitOnFailure(hr, "Failed to get Wix4FileShare.Wix4FileShare");
158 hr = ::StringCchCopyW(pss->wzId, countof(pss->wzId), pwzData);
159 ExitOnFailure(hr, "Failed to copy ID string to smb object");
160
161 hr = WcaGetRecordFormattedString(hRec, ssqShareName, &pwzData);
162 ExitOnFailure(hr, "Failed to get Wix4FileShare.ShareName");
163 hr = ::StringCchCopyW(pss->wzShareName, countof(pss->wzShareName), pwzData);
164 ExitOnFailure(hr, "Failed to copy share name string to smb object");
165
166 hr = WcaGetRecordString(hRec, ssqComponent, &pwzData);
167 ExitOnFailure(hr, "Failed to get Component for Wix4FileShare: '%ls'", pss->wzShareName);
168 hr = ::StringCchCopyW(pss->wzComponent, countof(pss->wzComponent), pwzData);
169 ExitOnFailure(hr, "Failed to copy component string to smb object");
170
171 hr = WcaGetRecordFormattedString(hRec, ssqDescription, &pwzData);
172 ExitOnFailure(hr, "Failed to get Share Description for Wix4FileShare: '%ls'", pss->wzShareName);
173 hr = ::StringCchCopyW(pss->wzDescription, countof(pss->wzDescription), pwzData);
174 ExitOnFailure(hr, "Failed to copy description string to smb object");
175
176 // get user info from the user table
177 hr = WcaGetRecordFormattedString(hRec, ssqUser, &pwzData);
178 ExitOnFailure(hr, "Failed to get Wix4User record for Wix4FileShare: '%ls'", pss->wzShareName);
179
180 // get component install state
181 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pss->wzComponent, &pss->isInstalled, &pss->isAction);
182 hr = HRESULT_FROM_WIN32(er);
183 ExitOnFailure(hr, "Failed to get Component state for Wix4FileShare");
184
185 // if a user was specified
186 if (*pwzData)
187 {
188 pss->fUseIntegratedAuth = FALSE;
189 pss->fLegacyUserProvided = TRUE;
190 hr = ScaGetUser(pwzData, &pss->scau);
191 ExitOnFailure(hr, "Failed to get user information for fileshare: '%ls'", pss->wzShareName);
192 }
193 else
194 {
195 pss->fLegacyUserProvided = FALSE;
196 // TODO: figure out whether this is useful still
197 //pss->fUseIntegratedAuth = TRUE;
198 // integrated authorization doesn't have a User record
199 }
200
201 // get the share's directory
202 hr = WcaGetRecordString(hRec, ssqDirectory, &pwzData);
203 ExitOnFailure(hr, "Failed to get directory for Wix4FileShare: '%ls'", pss->wzShareName);
204
205 WCHAR wzPath[MAX_PATH];
206 DWORD dwLen;
207 dwLen = countof(wzPath);
208 // review: relevant for file shares?
209 if (INSTALLSTATE_SOURCE == pss->isAction)
210 {
211 er = ::MsiGetSourcePathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
212 }
213 else
214 {
215 er = ::MsiGetTargetPathW(WcaGetInstallHandle(), pwzData, wzPath, &dwLen);
216 }
217 hr = HRESULT_FROM_WIN32(er);
218 ExitOnFailure(hr, "Failed to get Source/TargetPath for Directory");
219
220 // If the path is to the root of a drive, then it needs a trailing backslash.
221 // Otherwise, it can't have a trailing backslash.
222 if (3 < dwLen)
223 {
224 if (wzPath[dwLen - 1] == L'\\')
225 {
226 wzPath[dwLen - 1] = 0;
227 }
228 }
229 else if (2 == dwLen && wzPath[1] == L':')
230 {
231 wzPath[2] = L'\\';
232 wzPath[3] = 0;
233 }
234
235 hr = ::StringCchCopyW(pss->wzDirectory, countof(pss->wzDirectory), wzPath);
236 ExitOnFailure(hr, "Failed to copy directory string to smb object");
237
238 hr = WcaGetRecordInteger(hRec, ssqPermissions, &pss->nPermissions);
239 ExitOnFailure(hr, "Failed to get Wix4FileShare.Permissions");
240
241 // Check to see if additional user & permissions are specified for this share
242 if (bUserPermissionsTableExists)
243 {
244 hr = ScaSmbExPermsRead(pss);
245 ExitOnFailure(hr, "Failed to get Additional File Share Permissions");
246 }
247
248 *ppssList = AddSmbToList(*ppssList, pss);
249 pss = NULL; // set the smb NULL so it doesn't accidentally get freed below
250 }
251
252 if (E_NOMOREITEMS == hr)
253 {
254 hr = S_OK;
255 }
256 ExitOnFailure(hr, "Failure occured while processing Wix4FileShare table");
257
258LExit:
259 // if anything was left over after an error clean it all up
260 if (pss)
261 {
262 ScaSmbFreeList(pss);
263 }
264
265 ReleaseStr(pwzData);
266
267 return hr;
268}
269
270
271/********************************************************************
272 RetrieveSMBShareUserPermList - retrieve SMB Share's user permission list
273
274********************************************************************/
275HRESULT RetrieveFileShareUserPerm(SCA_SMB* pss, SCA_SMB_EX_USER_PERMS** ppExUserPermsList, DWORD *pUserPermsCount)
276{
277 HRESULT hr = S_OK;
278 SHARE_INFO_502* psi = NULL;
279 NET_API_STATUS s;
280 BOOL bValid, bDaclDefaulted;
281 PACL acl = NULL;
282 PEXPLICIT_ACCESSW pEA = NULL;
283 ULONG nCount = 0;
284 DWORD er = ERROR_SUCCESS;
285 PSID pSID = NULL;
286 DWORD nUserNameSize = MAX_DARWIN_COLUMN;
287 DWORD nDomainNameSize = MAX_DARWIN_COLUMN;
288 SID_NAME_USE peUse;
289 DWORD dwCounter = 0;
290 SCA_SMB_EX_USER_PERMS* pExUserPermsList = NULL;
291 DWORD dwUserPermsCount = 0;
292
293 *pUserPermsCount = 0;
294 s = ::NetShareGetInfo(NULL, pss->wzShareName, 502, (LPBYTE*)&psi);
295 WcaLog(LOGMSG_VERBOSE, "retrieving permissions on existing file share.");
296 if (NERR_NetNameNotFound == s)
297 {
298 WcaLog(LOGMSG_VERBOSE, "File share has already been removed.");
299 ExitFunction1(hr = S_OK);
300 }
301 else if (NERR_Success != s || psi == NULL)
302 {
303 hr = E_FAIL;
304 ExitOnFailure(hr, "Failed to get share information with return code: %d", s);
305 }
306 if (!::GetSecurityDescriptorDacl(psi->shi502_security_descriptor, &bValid, &acl, &bDaclDefaulted) || !bValid)
307 {
308 ExitOnLastError(hr, "Failed to get acl from security descriptor");
309 }
310
311 er = ::GetExplicitEntriesFromAclW(acl, &nCount, &pEA);
312 hr = HRESULT_FROM_WIN32(er);
313 ExitOnFailure(hr, "Failed to get access entries from acl for file share %ls", pss->wzShareName);
314 for (dwCounter = 0; dwCounter < nCount; ++dwCounter)
315 {
316 if (TRUSTEE_IS_SID == pEA[dwCounter].Trustee.TrusteeForm)
317 {
318 SCA_SMB_EX_USER_PERMS* pExUserPerms = NewExUserPermsSmb();
319 ::ZeroMemory(pExUserPerms, sizeof(*pExUserPerms));
320 pExUserPermsList = AddExUserPermsSmbToList(pExUserPermsList, pExUserPerms);
321 pSID = (PSID)(pEA[dwCounter].Trustee.ptstrName);
322 if (!::LookupAccountSidW(NULL, pSID, pExUserPerms->scau.wzName, &nUserNameSize, pExUserPerms->scau.wzDomain, &nDomainNameSize, &peUse))
323 {
324 hr = E_FAIL;
325 ExitOnFailure(hr, "Failed to get account name from SID");
326 }
327 pExUserPerms->nPermissions = pEA[dwCounter].grfAccessPermissions;
328 pExUserPerms->accessMode = pEA[dwCounter].grfAccessMode;
329 ++dwUserPermsCount;
330 nUserNameSize = MAX_DARWIN_COLUMN;
331 nDomainNameSize = MAX_DARWIN_COLUMN;
332 }
333 }
334 *ppExUserPermsList = pExUserPermsList;
335 *pUserPermsCount = dwUserPermsCount;
336
337LExit:
338 if (psi)
339 {
340 ::NetApiBufferFree(psi);
341 }
342
343 if (pEA)
344 {
345 ::LocalFree(pEA);
346 }
347
348 return hr;
349}
350
351
352/********************************************************************
353 SchedCreateSmb - schedule one instance of a file share creation
354
355********************************************************************/
356HRESULT SchedCreateSmb(SCA_SMB* pss)
357{
358 HRESULT hr = S_OK;
359
360 WCHAR wzDomainUser[255]; // "domain\user"
361 SCA_SMB_EX_USER_PERMS* pExUserPermsList = NULL;
362 int nCounter = 0;
363 WCHAR* pwzRollbackCustomActionData = NULL;
364 WCHAR* pwzCustomActionData = NULL;
365
366 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzRollbackCustomActionData);
367 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
368
369 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzCustomActionData);
370 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
371
372 hr = WcaWriteStringToCaData(pss->wzDescription, &pwzCustomActionData);
373 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
374
375 hr = WcaWriteStringToCaData(pss->wzDirectory, &pwzCustomActionData);
376 ExitOnFailure(hr, "Failed to add full path instance to CustomActionData");
377
378 hr = WcaWriteStringToCaData(pss->fUseIntegratedAuth ? L"1" : L"0", &pwzCustomActionData);
379 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
380
381 if (pss->fLegacyUserProvided)
382 {
383 hr = WcaWriteIntegerToCaData(pss->nUserPermissionCount + 1, &pwzCustomActionData);
384 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
385
386 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pss->scau.wzName, pss->scau.wzDomain);
387 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
388 hr = WcaWriteStringToCaData(wzDomainUser, &pwzCustomActionData);
389 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
390
391 hr = WcaWriteIntegerToCaData(pss->nPermissions, &pwzCustomActionData);
392 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
393 }
394 else
395 {
396 hr = WcaWriteIntegerToCaData(pss->nUserPermissionCount, &pwzCustomActionData);
397 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
398 }
399
400 if (pss->nUserPermissionCount > 0)
401 {
402 nCounter = 0;
403 for (pExUserPermsList = pss->pExUserPerms; pExUserPermsList; pExUserPermsList = pExUserPermsList->pExUserPermsNext)
404 {
405 Assert(nCounter < pss->nUserPermissionCount);
406
407 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pExUserPermsList->scau.wzName, pExUserPermsList->scau.wzDomain);
408 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
409 hr = WcaWriteStringToCaData(wzDomainUser, &pwzCustomActionData);
410 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
411
412 hr = WcaWriteIntegerToCaData((int)pExUserPermsList->accessMode, &pwzCustomActionData);
413 ExitOnFailure(hr, "Failed to add access mode to CustomActionData");
414
415 hr = WcaWriteIntegerToCaData(pExUserPermsList->nPermissions, &pwzCustomActionData);
416 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
417 ++nCounter;
418 }
419 Assert(nCounter == pss->nUserPermissionCount);
420 }
421
422 // Schedule the rollback first
423 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateSmbRollback"), pwzRollbackCustomActionData, COST_SMB_DROPSMB);
424 ExitOnFailure(hr, "Failed to schedule DropSmb action");
425
426 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateSmb"), pwzCustomActionData, COST_SMB_CREATESMB);
427 ExitOnFailure(hr, "Failed to schedule CreateSmb action");
428
429LExit:
430 ReleaseStr(pwzRollbackCustomActionData);
431 ReleaseStr(pwzCustomActionData);
432
433 if (pExUserPermsList)
434 {
435 ScaExUserPermsSmbFreeList(pExUserPermsList);
436 }
437
438 return hr;
439}
440
441
442/********************************************************************
443 ScaSmbInstall - for every file share, schedule the create custom action
444
445********************************************************************/
446HRESULT ScaSmbInstall(SCA_SMB* pssList)
447{
448 HRESULT hr = S_FALSE; // assume nothing will be done
449 SCA_SMB* pss = NULL;
450
451 for (pss = pssList; pss; pss = pss->pssNext)
452 {
453 // if installing this component
454 if (WcaIsInstalling(pss->isInstalled, pss->isAction) )
455 {
456 hr = SchedCreateSmb(pss);
457 ExitOnFailure(hr, "Failed to schedule the creation of the fileshare: %ls", pss->wzShareName);
458 }
459 }
460
461LExit:
462 return hr;
463}
464
465
466/********************************************************************
467 SchedDropSmb - schedule one instance of a file share removal
468
469********************************************************************/
470HRESULT SchedDropSmb(SCA_SMB* pss)
471{
472 HRESULT hr = S_OK;
473
474 WCHAR* pwzCustomActionData = NULL;
475 WCHAR* pwzRollbackCustomActionData = NULL;
476 SCA_SMB_EX_USER_PERMS *pExUserPermsList = NULL;
477 SCA_SMB_EX_USER_PERMS *pExUserPerm = NULL;
478 WCHAR wzDomainUser[255]; // "domain\user"
479 DWORD dwUserPermsCount = 0;
480
481 // roll back DropSmb
482 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzRollbackCustomActionData);
483 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
484
485 hr = WcaWriteStringToCaData(pss->wzDescription, &pwzRollbackCustomActionData);
486 ExitOnFailure(hr, "Failed to add server name to CustomActionData");
487
488 hr = WcaWriteStringToCaData(pss->wzDirectory, &pwzRollbackCustomActionData);
489 ExitOnFailure(hr, "Failed to add full path instance to CustomActionData");
490
491 hr = WcaWriteStringToCaData(L"1", &pwzRollbackCustomActionData);
492 ExitOnFailure(hr, "Failed to add useintegrated flag to CustomActionData");
493
494 hr = RetrieveFileShareUserPerm(pss, &pExUserPermsList, &dwUserPermsCount);
495 ExitOnFailure(hr, "Failed to retrieve SMBShare's user permissions");
496
497 hr = WcaWriteIntegerToCaData((int)dwUserPermsCount, &pwzRollbackCustomActionData);
498 ExitOnFailure(hr, "Failed to add additional user permission count to CustomActionData");
499
500 for (pExUserPerm = pExUserPermsList; pExUserPerm; pExUserPerm = pExUserPerm->pExUserPermsNext)
501 {
502 hr = UserBuildDomainUserName(wzDomainUser, countof(wzDomainUser), pExUserPerm->scau.wzName, pExUserPerm->scau.wzDomain);
503 ExitOnFailure(hr, "Failed to build user and domain name for CustomActionData");
504 hr = WcaWriteStringToCaData(wzDomainUser, &pwzRollbackCustomActionData);
505 ExitOnFailure(hr, "Failed to add server Domain\\UserName to CustomActionData");
506
507 hr = WcaWriteIntegerToCaData((int)pExUserPerm->accessMode, &pwzRollbackCustomActionData);
508 ExitOnFailure(hr, "Failed to add access mode to CustomActionData");
509
510 hr = WcaWriteIntegerToCaData(pExUserPerm->nPermissions, &pwzRollbackCustomActionData);
511 ExitOnFailure(hr, "Failed to add permissions to CustomActionData");
512 }
513
514 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"DropSmbRollback"), pwzRollbackCustomActionData, COST_SMB_CREATESMB);
515 ExitOnFailure(hr, "Failed to schedule DropSmbRollback action");
516
517 // DropSMB
518 hr = WcaWriteStringToCaData(pss->wzShareName, &pwzCustomActionData);
519 ExitOnFailure(hr, "failed to add ShareName to CustomActionData");
520
521 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"DropSmb"), pwzCustomActionData, COST_SMB_DROPSMB);
522 ExitOnFailure(hr, "Failed to schedule DropSmb action");
523
524LExit:
525 ReleaseStr(pwzCustomActionData);
526
527 if (pExUserPermsList)
528 {
529 ScaExUserPermsSmbFreeList(pExUserPermsList);
530 }
531
532 return hr;
533
534}
535
536
537/********************************************************************
538 ScaSmbUninstall - for every file share, schedule the drop custom action
539
540********************************************************************/
541HRESULT ScaSmbUninstall(SCA_SMB* pssList)
542{
543 HRESULT hr = S_FALSE; // assume nothing will be done
544 SCA_SMB* pss = NULL;
545
546 for (pss = pssList; pss; pss = pss->pssNext)
547 {
548 // if uninstalling this component
549 if (WcaIsUninstalling(pss->isInstalled, pss->isAction) )
550 {
551 hr = SchedDropSmb(pss);
552 ExitOnFailure(hr, "Failed to remove file share %ls", pss->wzShareName);
553 }
554 }
555
556LExit:
557 return hr;
558}
559
560LPCWSTR vcsSmbExUserPermsQuery = L"SELECT `FileShare_`,`User_`,`Permissions` "
561 L"FROM `Wix4FileSharePermissions` WHERE `FileShare_`=?";
562
563enum eSmbUserPermsQuery {
564 ssupqFileShare = 1,
565 ssupqUser,
566 ssupqPermissions
567
568};
569
570
571/********************************************************************
572 ScaSmbExPermsRead - for Every entry in File Permissions table add a
573 User Name & Permissions structure to the List
574
575********************************************************************/
576HRESULT ScaSmbExPermsRead(SCA_SMB* pss)
577{
578 HRESULT hr = S_OK;
579 PMSIHANDLE hView, hRec;
580
581 LPWSTR pwzData = NULL;
582 SCA_SMB_EX_USER_PERMS* pExUserPermsList = pss->pExUserPerms;
583 SCA_SMB_EX_USER_PERMS* pExUserPerms = NULL;
584 int nCounter = 0;
585
586 hRec = ::MsiCreateRecord(1);
587 hr = WcaSetRecordString(hRec, 1, pss->wzId);
588 ExitOnFailure(hr, "Failed to look up FileShare");
589
590 hr = WcaOpenView(vcsSmbExUserPermsQuery, &hView);
591 ExitOnFailure(hr, "Failed to open view on Wix4FileSharePermissions table");
592 hr = WcaExecuteView(hView, hRec);
593 ExitOnFailure(hr, "Failed to execute view on Wix4FileSharePermissions table");
594
595 // loop through all User/Permissions paris returned
596 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
597 {
598 pExUserPerms = NewExUserPermsSmb();
599 if (!pExUserPerms)
600 {
601 hr = E_OUTOFMEMORY;
602 break;
603 }
604 Assert(pExUserPerms);
605 ::ZeroMemory(pExUserPerms, sizeof(*pExUserPerms));
606
607 hr = WcaGetRecordString(hRec, ssupqUser, &pwzData);
608 ExitOnFailure(hr, "Failed to get Wix4FileSharePermissions.User");
609 hr = ScaGetUser(pwzData, &pExUserPerms->scau);
610 ExitOnFailure(hr, "Failed to get user information for fileshare: '%ls'", pss->wzShareName);
611
612 hr = WcaGetRecordInteger(hRec, ssupqPermissions, &pExUserPerms->nPermissions);
613 ExitOnFailure(hr, "Failed to get Wix4FileSharePermissions.Permissions");
614 pExUserPerms->accessMode = SET_ACCESS; // we only support SET_ACCESS here
615
616 pExUserPermsList = AddExUserPermsSmbToList(pExUserPermsList, pExUserPerms);
617 ++nCounter;
618 pExUserPerms = NULL; // set the smb NULL so it doesn't accidentally get freed below
619 }
620
621 if (E_NOMOREITEMS == hr)
622 {
623 hr = S_OK;
624 pss->pExUserPerms = pExUserPermsList;
625 pss->nUserPermissionCount = nCounter;
626 }
627 ExitOnFailure(hr, "Failure occured while processing FileShare table");
628
629LExit:
630 // if anything was left over after an error clean it all up
631 if (pExUserPerms)
632 {
633 ScaExUserPermsSmbFreeList(pExUserPerms);
634 }
635
636 ReleaseStr(pwzData);
637
638 return hr;
639}
diff --git a/src/ext/Util/ca/scauser.cpp b/src/ext/Util/ca/scauser.cpp
new file mode 100644
index 00000000..b25e9daf
--- /dev/null
+++ b/src/ext/Util/ca/scauser.cpp
@@ -0,0 +1,709 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5LPCWSTR vcsUserQuery = L"SELECT `Wix4User`, `Component_`, `Name`, `Domain`, `Password` FROM `Wix4User` WHERE `Wix4User`=?";
6enum eUserQuery { vuqUser = 1, vuqComponent, vuqName, vuqDomain, vuqPassword };
7
8LPCWSTR vcsGroupQuery = L"SELECT `Wix4Group`, `Component_`, `Name`, `Domain` FROM `Wix4Group` WHERE `Wix4Group`=?";
9enum eGroupQuery { vgqGroup = 1, vgqComponent, vgqName, vgqDomain };
10
11LPCWSTR vcsUserGroupQuery = L"SELECT `Wix4User_`, `Wix4Group_` FROM `Wix4UserGroup` WHERE `Wix4User_`=?";
12enum eUserGroupQuery { vugqUser = 1, vugqGroup };
13
14LPCWSTR vActionableQuery = L"SELECT `Wix4User`,`Component_`,`Name`,`Domain`,`Password`,`Attributes` FROM `Wix4User` WHERE `Component_` IS NOT NULL";
15enum eActionableQuery { vaqUser = 1, vaqComponent, vaqName, vaqDomain, vaqPassword, vaqAttributes };
16
17
18static HRESULT AddUserToList(
19 __inout SCA_USER** ppsuList
20 );
21
22static HRESULT AddGroupToList(
23 __inout SCA_GROUP** ppsgList
24 );
25
26
27HRESULT __stdcall ScaGetUser(
28 __in LPCWSTR wzUser,
29 __out SCA_USER* pscau
30 )
31{
32 if (!wzUser || !pscau)
33 {
34 return E_INVALIDARG;
35 }
36
37 HRESULT hr = S_OK;
38 PMSIHANDLE hView, hRec;
39
40 LPWSTR pwzData = NULL;
41
42 // clear struct and bail right away if no user key was passed to search for
43 ::ZeroMemory(pscau, sizeof(*pscau));
44 if (!*wzUser)
45 {
46 ExitFunction1(hr = S_OK);
47 }
48
49 hRec = ::MsiCreateRecord(1);
50 hr = WcaSetRecordString(hRec, 1, wzUser);
51 ExitOnFailure(hr, "Failed to look up User");
52
53 hr = WcaOpenView(vcsUserQuery, &hView);
54 ExitOnFailure(hr, "Failed to open view on Wix4User table");
55 hr = WcaExecuteView(hView, hRec);
56 ExitOnFailure(hr, "Failed to execute view on Wix4User table");
57
58 hr = WcaFetchSingleRecord(hView, &hRec);
59 if (S_OK == hr)
60 {
61 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
62 ExitOnFailure(hr, "Failed to get Wix4User.User");
63 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
64 ExitOnFailure(hr, "Failed to copy key string to user object");
65
66 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
67 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
68 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
69 ExitOnFailure(hr, "Failed to copy component string to user object");
70
71 hr = WcaGetRecordFormattedString(hRec, vuqName, &pwzData);
72 ExitOnFailure(hr, "Failed to get Wix4User.Name");
73 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
74 ExitOnFailure(hr, "Failed to copy name string to user object");
75
76 hr = WcaGetRecordFormattedString(hRec, vuqDomain, &pwzData);
77 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
78 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
79 ExitOnFailure(hr, "Failed to copy domain string to user object");
80
81 hr = WcaGetRecordFormattedString(hRec, vuqPassword, &pwzData);
82 ExitOnFailure(hr, "Failed to get Wix4User.Password");
83 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
84 ExitOnFailure(hr, "Failed to copy password string to user object");
85 }
86 else if (E_NOMOREITEMS == hr)
87 {
88 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
89 hr = E_FAIL;
90 }
91 else
92 {
93 ExitOnFailure(hr, "Error or found multiple matching Wix4User rows");
94 }
95
96LExit:
97 ReleaseStr(pwzData);
98
99 return hr;
100}
101
102HRESULT __stdcall ScaGetUserDeferred(
103 __in LPCWSTR wzUser,
104 __in WCA_WRAPQUERY_HANDLE hUserQuery,
105 __out SCA_USER* pscau
106 )
107{
108 if (!wzUser || !pscau)
109 {
110 return E_INVALIDARG;
111 }
112
113 HRESULT hr = S_OK;
114 MSIHANDLE hRec, hRecTest;
115
116 LPWSTR pwzData = NULL;
117
118 // clear struct and bail right away if no user key was passed to search for
119 ::ZeroMemory(pscau, sizeof(*pscau));
120 if (!*wzUser)
121 {
122 ExitFunction1(hr = S_OK);
123 }
124
125 // Reset back to the first record
126 WcaFetchWrappedReset(hUserQuery);
127
128 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRec);
129 if (S_OK == hr)
130 {
131 hr = WcaFetchWrappedRecordWhereString(hUserQuery, vuqUser, wzUser, &hRecTest);
132 if (S_OK == hr)
133 {
134 AssertSz(FALSE, "Found multiple matching Wix4User rows");
135 }
136
137 hr = WcaGetRecordString(hRec, vuqUser, &pwzData);
138 ExitOnFailure(hr, "Failed to get Wix4User.User");
139 hr = ::StringCchCopyW(pscau->wzKey, countof(pscau->wzKey), pwzData);
140 ExitOnFailure(hr, "Failed to copy key string to user object (in deferred CA)");
141
142 hr = WcaGetRecordString(hRec, vuqComponent, &pwzData);
143 ExitOnFailure(hr, "Failed to get Wix4User.Component_");
144 hr = ::StringCchCopyW(pscau->wzComponent, countof(pscau->wzComponent), pwzData);
145 ExitOnFailure(hr, "Failed to copy component string to user object (in deferred CA)");
146
147 hr = WcaGetRecordString(hRec, vuqName, &pwzData);
148 ExitOnFailure(hr, "Failed to get Wix4User.Name");
149 hr = ::StringCchCopyW(pscau->wzName, countof(pscau->wzName), pwzData);
150 ExitOnFailure(hr, "Failed to copy name string to user object (in deferred CA)");
151
152 hr = WcaGetRecordString(hRec, vuqDomain, &pwzData);
153 ExitOnFailure(hr, "Failed to get Wix4User.Domain");
154 hr = ::StringCchCopyW(pscau->wzDomain, countof(pscau->wzDomain), pwzData);
155 ExitOnFailure(hr, "Failed to copy domain string to user object (in deferred CA)");
156
157 hr = WcaGetRecordString(hRec, vuqPassword, &pwzData);
158 ExitOnFailure(hr, "Failed to get Wix4User.Password");
159 hr = ::StringCchCopyW(pscau->wzPassword, countof(pscau->wzPassword), pwzData);
160 ExitOnFailure(hr, "Failed to copy password string to user object (in deferred CA)");
161 }
162 else if (E_NOMOREITEMS == hr)
163 {
164 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4User.User='%ls'", wzUser);
165 hr = E_FAIL;
166 }
167 else
168 {
169 ExitOnFailure(hr, "Error fetching single Wix4User row");
170 }
171
172LExit:
173 ReleaseStr(pwzData);
174
175 return hr;
176}
177
178
179HRESULT __stdcall ScaGetGroup(
180 __in LPCWSTR wzGroup,
181 __out SCA_GROUP* pscag
182 )
183{
184 if (!wzGroup || !pscag)
185 {
186 return E_INVALIDARG;
187 }
188
189 HRESULT hr = S_OK;
190 PMSIHANDLE hView, hRec;
191
192 LPWSTR pwzData = NULL;
193
194 hRec = ::MsiCreateRecord(1);
195 hr = WcaSetRecordString(hRec, 1, wzGroup);
196 ExitOnFailure(hr, "Failed to look up Group");
197
198 hr = WcaOpenView(vcsGroupQuery, &hView);
199 ExitOnFailure(hr, "Failed to open view on Wix4Group table");
200 hr = WcaExecuteView(hView, hRec);
201 ExitOnFailure(hr, "Failed to execute view on Wix4Group table");
202
203 hr = WcaFetchSingleRecord(hView, &hRec);
204 if (S_OK == hr)
205 {
206 hr = WcaGetRecordString(hRec, vgqGroup, &pwzData);
207 ExitOnFailure(hr, "Failed to get Wix4Group.Wix4Group.");
208 hr = ::StringCchCopyW(pscag->wzKey, countof(pscag->wzKey), pwzData);
209 ExitOnFailure(hr, "Failed to copy Wix4Group.Wix4Group.");
210
211 hr = WcaGetRecordString(hRec, vgqComponent, &pwzData);
212 ExitOnFailure(hr, "Failed to get Wix4Group.Component_");
213 hr = ::StringCchCopyW(pscag->wzComponent, countof(pscag->wzComponent), pwzData);
214 ExitOnFailure(hr, "Failed to copy Wix4Group.Component_.");
215
216 hr = WcaGetRecordFormattedString(hRec, vgqName, &pwzData);
217 ExitOnFailure(hr, "Failed to get Wix4Group.Name");
218 hr = ::StringCchCopyW(pscag->wzName, countof(pscag->wzName), pwzData);
219 ExitOnFailure(hr, "Failed to copy Wix4Group.Name.");
220
221 hr = WcaGetRecordFormattedString(hRec, vgqDomain, &pwzData);
222 ExitOnFailure(hr, "Failed to get Wix4Group.Domain");
223 hr = ::StringCchCopyW(pscag->wzDomain, countof(pscag->wzDomain), pwzData);
224 ExitOnFailure(hr, "Failed to copy Wix4Group.Domain.");
225 }
226 else if (E_NOMOREITEMS == hr)
227 {
228 WcaLog(LOGMSG_STANDARD, "Error: Cannot locate Wix4Group.Wix4Group='%ls'", wzGroup);
229 hr = E_FAIL;
230 }
231 else
232 {
233 ExitOnFailure(hr, "Error or found multiple matching Wix4Group rows");
234 }
235
236LExit:
237 ReleaseStr(pwzData);
238
239 return hr;
240}
241
242
243void ScaUserFreeList(
244 __in SCA_USER* psuList
245 )
246{
247 SCA_USER* psuDelete = psuList;
248 while (psuList)
249 {
250 psuDelete = psuList;
251 psuList = psuList->psuNext;
252
253 ScaGroupFreeList(psuDelete->psgGroups);
254 MemFree(psuDelete);
255 }
256}
257
258
259void ScaGroupFreeList(
260 __in SCA_GROUP* psgList
261 )
262{
263 SCA_GROUP* psgDelete = psgList;
264 while (psgList)
265 {
266 psgDelete = psgList;
267 psgList = psgList->psgNext;
268
269 MemFree(psgDelete);
270 }
271}
272
273
274HRESULT ScaUserRead(
275 __out SCA_USER** ppsuList
276 )
277{
278 //Assert(FALSE);
279 Assert(ppsuList);
280
281 HRESULT hr = S_OK;
282 UINT er = ERROR_SUCCESS;
283 PMSIHANDLE hView, hRec, hUserRec, hUserGroupView;
284
285 LPWSTR pwzData = NULL;
286
287 BOOL fUserGroupExists = FALSE;
288
289 SCA_USER *psu = NULL;
290
291 INSTALLSTATE isInstalled, isAction;
292
293 if (S_OK != WcaTableExists(L"Wix4User"))
294 {
295 WcaLog(LOGMSG_VERBOSE, "Wix4User Table does not exist, exiting");
296 ExitFunction1(hr = S_FALSE);
297 }
298
299 if (S_OK == WcaTableExists(L"Wix4UserGroup"))
300 {
301 fUserGroupExists = TRUE;
302 }
303
304 //
305 // loop through all the users
306 //
307 hr = WcaOpenExecuteView(vActionableQuery, &hView);
308 ExitOnFailure(hr, "failed to open view on Wix4User table");
309 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
310 {
311 hr = WcaGetRecordString(hRec, vaqComponent, &pwzData);
312 ExitOnFailure(hr, "failed to get Wix4User.Component");
313
314 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), pwzData, &isInstalled, &isAction);
315 hr = HRESULT_FROM_WIN32(er);
316 ExitOnFailure(hr, "failed to get Component state for Wix4User");
317
318 // don't bother if we aren't installing or uninstalling this component
319 if (WcaIsInstalling(isInstalled, isAction) || WcaIsUninstalling(isInstalled, isAction))
320 {
321 //
322 // Add the user to the list and populate it's values
323 //
324 hr = AddUserToList(ppsuList);
325 ExitOnFailure(hr, "failed to add user to list");
326
327 psu = *ppsuList;
328
329 psu->isInstalled = isInstalled;
330 psu->isAction = isAction;
331 hr = ::StringCchCopyW(psu->wzComponent, countof(psu->wzComponent), pwzData);
332 ExitOnFailure(hr, "failed to copy component name: %ls", pwzData);
333
334 hr = WcaGetRecordString(hRec, vaqUser, &pwzData);
335 ExitOnFailure(hr, "failed to get Wix4User.User");
336 hr = ::StringCchCopyW(psu->wzKey, countof(psu->wzKey), pwzData);
337 ExitOnFailure(hr, "failed to copy user key: %ls", pwzData);
338
339 hr = WcaGetRecordFormattedString(hRec, vaqName, &pwzData);
340 ExitOnFailure(hr, "failed to get Wix4User.Name");
341 hr = ::StringCchCopyW(psu->wzName, countof(psu->wzName), pwzData);
342 ExitOnFailure(hr, "failed to copy user name: %ls", pwzData);
343
344 hr = WcaGetRecordFormattedString(hRec, vaqDomain, &pwzData);
345 ExitOnFailure(hr, "failed to get Wix4User.Domain");
346 hr = ::StringCchCopyW(psu->wzDomain, countof(psu->wzDomain), pwzData);
347 ExitOnFailure(hr, "failed to copy user domain: %ls", pwzData);
348
349 hr = WcaGetRecordFormattedString(hRec, vaqPassword, &pwzData);
350 ExitOnFailure(hr, "failed to get Wix4User.Password");
351 hr = ::StringCchCopyW(psu->wzPassword, countof(psu->wzPassword), pwzData);
352 ExitOnFailure(hr, "failed to copy user password");
353
354 hr = WcaGetRecordInteger(hRec, vaqAttributes, &psu->iAttributes);
355 ExitOnFailure(hr, "failed to get Wix4User.Attributes");
356
357 // Check if this user is to be added to any groups
358 if (fUserGroupExists)
359 {
360 hUserRec = ::MsiCreateRecord(1);
361 hr = WcaSetRecordString(hUserRec, 1, psu->wzKey);
362 ExitOnFailure(hr, "Failed to create user record for querying Wix4UserGroup table");
363
364 hr = WcaOpenView(vcsUserGroupQuery, &hUserGroupView);
365 ExitOnFailure(hr, "Failed to open view on Wix4UserGroup table for user %ls", psu->wzKey);
366 hr = WcaExecuteView(hUserGroupView, hUserRec);
367 ExitOnFailure(hr, "Failed to execute view on Wix4UserGroup table for user: %ls", psu->wzKey);
368
369 while (S_OK == (hr = WcaFetchRecord(hUserGroupView, &hRec)))
370 {
371 hr = WcaGetRecordString(hRec, vugqGroup, &pwzData);
372 ExitOnFailure(hr, "failed to get Wix4UserGroup.Group");
373
374 hr = AddGroupToList(&(psu->psgGroups));
375 ExitOnFailure(hr, "failed to add group to list");
376
377 hr = ScaGetGroup(pwzData, psu->psgGroups);
378 ExitOnFailure(hr, "failed to get information for group: %ls", pwzData);
379 }
380
381 if (E_NOMOREITEMS == hr)
382 {
383 hr = S_OK;
384 }
385 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4UserGroup table");
386 }
387 }
388 }
389
390 if (E_NOMOREITEMS == hr)
391 {
392 hr = S_OK;
393 }
394 ExitOnFailure(hr, "failed to enumerate selected rows from Wix4User table");
395
396LExit:
397 ReleaseStr(pwzData);
398
399 return hr;
400}
401
402
403static HRESULT WriteGroupInfo(
404 __in SCA_GROUP* psgList,
405 __in LPWSTR *ppwzActionData
406 )
407{
408 HRESULT hr = S_OK;
409
410 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
411 {
412 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
413 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
414
415 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
416 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
417 }
418
419LExit:
420 return hr;
421}
422
423
424// Behaves like WriteGroupInfo, but it filters out groups the user is currently a member of,
425// because we don't want to rollback those
426static HRESULT WriteGroupRollbackInfo(
427 __in LPCWSTR pwzName,
428 __in LPCWSTR pwzDomain,
429 __in SCA_GROUP* psgList,
430 __in LPWSTR *ppwzActionData
431 )
432{
433 HRESULT hr = S_OK;
434 BOOL fIsMember = FALSE;
435
436 for (SCA_GROUP* psg = psgList; psg; psg = psg->psgNext)
437 {
438 hr = UserCheckIsMember(pwzName, pwzDomain, psg->wzName, psg->wzDomain, &fIsMember);
439 if (FAILED(hr))
440 {
441 WcaLog(LOGMSG_VERBOSE, "Failed to check if user: %ls (domain: %ls) is member of a group while collecting rollback information (error code 0x%x) - continuing", pwzName, pwzDomain, hr);
442 hr = S_OK;
443 continue;
444 }
445
446 // If the user is currently a member, we don't want to undo that on rollback, so skip adding
447 // this group record to the list of groups to rollback
448 if (fIsMember)
449 {
450 continue;
451 }
452
453 hr = WcaWriteStringToCaData(psg->wzName, ppwzActionData);
454 ExitOnFailure(hr, "failed to add group name to custom action data: %ls", psg->wzName);
455
456 hr = WcaWriteStringToCaData(psg->wzDomain, ppwzActionData);
457 ExitOnFailure(hr, "failed to add group domain to custom action data: %ls", psg->wzDomain);
458 }
459
460LExit:
461 return hr;
462}
463
464
465/* ****************************************************************
466ScaUserExecute - Schedules user account creation or removal based on
467component state.
468
469******************************************************************/
470HRESULT ScaUserExecute(
471 __in SCA_USER *psuList
472 )
473{
474 HRESULT hr = S_OK;
475 DWORD er = 0;
476 PDOMAIN_CONTROLLER_INFOW pDomainControllerInfo = NULL;
477
478 LPWSTR pwzBaseScriptKey = NULL;
479 DWORD cScriptKey = 0;
480
481 USER_INFO_0 *pUserInfo = NULL;
482 LPWSTR pwzScriptKey = NULL;
483 LPWSTR pwzActionData = NULL;
484 LPWSTR pwzRollbackData = NULL;
485
486 // Get the base script key for this CustomAction.
487 hr = WcaCaScriptCreateKey(&pwzBaseScriptKey);
488 ExitOnFailure(hr, "Failed to get encoding key.");
489
490 // Loop through all the users to be configured.
491 for (SCA_USER *psu = psuList; psu; psu = psu->psuNext)
492 {
493 USER_EXISTS ueUserExists = USER_EXISTS_INDETERMINATE;
494
495 // Always put the User Name and Domain plus Attributes on the front of the CustomAction
496 // data. Sometimes we'll add more data.
497 Assert(psu->wzName);
498 hr = WcaWriteStringToCaData(psu->wzName, &pwzActionData);
499 ExitOnFailure(hr, "Failed to add user name to custom action data: %ls", psu->wzName);
500 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzActionData);
501 ExitOnFailure(hr, "Failed to add user domain to custom action data: %ls", psu->wzDomain);
502 hr = WcaWriteIntegerToCaData(psu->iAttributes, &pwzActionData);
503 ExitOnFailure(hr, "failed to add user attributes to custom action data for user: %ls", psu->wzKey);
504
505 // Check to see if the user already exists since we have to be very careful when adding
506 // and removing users. Note: MSDN says that it is safe to call these APIs from any
507 // user, so we should be safe calling it during immediate mode.
508 er = ::NetApiBufferAllocate(sizeof(USER_INFO_0), reinterpret_cast<LPVOID*>(&pUserInfo));
509 hr = HRESULT_FROM_WIN32(er);
510 ExitOnFailure(hr, "Failed to allocate memory to check existence of user: %ls", psu->wzName);
511
512 LPCWSTR wzDomain = psu->wzDomain;
513 if (wzDomain && *wzDomain)
514 {
515 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, NULL, &pDomainControllerInfo);
516 if (RPC_S_SERVER_UNAVAILABLE == er)
517 {
518 // MSDN says, if we get the above error code, try again with the "DS_FORCE_REDISCOVERY" flag
519 er = ::DsGetDcNameW(NULL, wzDomain, NULL, NULL, DS_FORCE_REDISCOVERY, &pDomainControllerInfo);
520 }
521 if (ERROR_SUCCESS == er)
522 {
523 wzDomain = pDomainControllerInfo->DomainControllerName + 2; //Add 2 so that we don't get the \\ prefix
524 }
525 }
526
527 er = ::NetUserGetInfo(wzDomain, psu->wzName, 0, reinterpret_cast<LPBYTE*>(pUserInfo));
528 if (NERR_Success == er)
529 {
530 ueUserExists = USER_EXISTS_YES;
531 }
532 else if (NERR_UserNotFound == er)
533 {
534 ueUserExists = USER_EXISTS_NO;
535 }
536 else
537 {
538 ueUserExists = USER_EXISTS_INDETERMINATE;
539 hr = HRESULT_FROM_WIN32(er);
540 WcaLog(LOGMSG_VERBOSE, "Failed to check existence of domain: %ls, user: %ls (error code 0x%x) - continuing", wzDomain, psu->wzName, hr);
541 hr = S_OK;
542 er = ERROR_SUCCESS;
543 }
544
545 if (WcaIsInstalling(psu->isInstalled, psu->isAction))
546 {
547 // If the user exists, check to see if we are supposed to fail if user the exists before
548 // the install.
549 if (USER_EXISTS_YES == ueUserExists)
550 {
551 // Reinstalls will always fail if we don't remove the check for "fail if exists".
552 if (WcaIsReInstalling(psu->isInstalled, psu->isAction))
553 {
554 psu->iAttributes &= ~SCAU_FAIL_IF_EXISTS;
555 }
556
557 if ((SCAU_FAIL_IF_EXISTS & (psu->iAttributes)) && !(SCAU_UPDATE_IF_EXISTS & (psu->iAttributes)))
558 {
559 hr = HRESULT_FROM_WIN32(NERR_UserExists);
560 MessageExitOnFailure(hr, msierrUSRFailedUserCreateExists, "Failed to create user: %ls because user already exists.", psu->wzName);
561 }
562 }
563
564 // Rollback only if the user already exists, we couldn't determine if the user exists, or we are going to create the user
565 if ((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists) || !(psu->iAttributes & SCAU_DONT_CREATE_USER))
566 {
567 ++cScriptKey;
568 hr = StrAllocFormatted(&pwzScriptKey, L"%ls%u", pwzBaseScriptKey, cScriptKey);
569 ExitOnFailure(hr, "Failed to create encoding key.");
570
571 // Write the script key to CustomActionData for install and rollback so information can be passed to rollback.
572 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzActionData);
573 ExitOnFailure(hr, "Failed to add encoding key to custom action data.");
574
575 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzRollbackData);
576 ExitOnFailure(hr, "Failed to add encoding key to rollback custom action data.");
577
578 INT iRollbackUserAttributes = psu->iAttributes;
579
580 // If the user already exists, ensure this is accounted for in rollback
581 if (USER_EXISTS_YES == ueUserExists)
582 {
583 iRollbackUserAttributes |= SCAU_DONT_CREATE_USER;
584 }
585 else
586 {
587 iRollbackUserAttributes &= ~SCAU_DONT_CREATE_USER;
588 }
589
590 // The deferred CA determines when to rollback User Rights Assignments so these should never be set.
591 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_SERVICE;
592 iRollbackUserAttributes &= ~SCAU_ALLOW_LOGON_AS_BATCH;
593
594 hr = WcaWriteStringToCaData(psu->wzName, &pwzRollbackData);
595 ExitOnFailure(hr, "Failed to add user name to rollback custom action data: %ls", psu->wzName);
596 hr = WcaWriteStringToCaData(psu->wzDomain, &pwzRollbackData);
597 ExitOnFailure(hr, "Failed to add user domain to rollback custom action data: %ls", psu->wzDomain);
598 hr = WcaWriteIntegerToCaData(iRollbackUserAttributes, &pwzRollbackData);
599 ExitOnFailure(hr, "failed to add user attributes to rollback custom action data for user: %ls", psu->wzKey);
600
601 // If the user already exists, add relevant group information to rollback data
602 if (USER_EXISTS_YES == ueUserExists || USER_EXISTS_INDETERMINATE == ueUserExists)
603 {
604 hr = WriteGroupRollbackInfo(psu->wzName, psu->wzDomain, psu->psgGroups, &pwzRollbackData);
605 ExitOnFailure(hr, "failed to add group information to rollback custom action data");
606 }
607
608 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUserRollback"), pwzRollbackData, COST_USER_DELETE);
609 ExitOnFailure(hr, "failed to schedule CreateUserRollback");
610 }
611 else
612 {
613 // Write empty script key to CustomActionData since there is no rollback.
614 hr = WcaWriteStringToCaData(L"", &pwzActionData);
615 ExitOnFailure(hr, "Failed to add empty encoding key to custom action data.");
616 }
617
618 //
619 // Schedule the creation now.
620 //
621 hr = WcaWriteStringToCaData(psu->wzPassword, &pwzActionData);
622 ExitOnFailure(hr, "failed to add user password to custom action data for user: %ls", psu->wzKey);
623
624 // Add user's group information to custom action data
625 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
626 ExitOnFailure(hr, "failed to add group information to custom action data");
627
628 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"CreateUser"), pwzActionData, COST_USER_ADD);
629 ExitOnFailure(hr, "failed to schedule CreateUser");
630 }
631 else if (((USER_EXISTS_YES == ueUserExists) || (USER_EXISTS_INDETERMINATE == ueUserExists)) && WcaIsUninstalling(psu->isInstalled, psu->isAction) && !(psu->iAttributes & SCAU_DONT_REMOVE_ON_UNINSTALL))
632 {
633 // Add user's group information - this will ensure the user can be removed from any groups they were added to, if the user isn't be deleted
634 hr = WriteGroupInfo(psu->psgGroups, &pwzActionData);
635 ExitOnFailure(hr, "failed to add group information to custom action data");
636
637 //
638 // Schedule the removal because the user exists and we don't have any flags set
639 // that say, don't remove the user on uninstall.
640 //
641 // Note: We can't rollback the removal of a user which is why RemoveUser is a commit
642 // CustomAction.
643 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RemoveUser"), pwzActionData, COST_USER_DELETE);
644 ExitOnFailure(hr, "failed to schedule RemoveUser");
645 }
646
647 ReleaseNullStr(pwzScriptKey);
648 ReleaseNullStr(pwzActionData);
649 ReleaseNullStr(pwzRollbackData);
650 if (pUserInfo)
651 {
652 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
653 pUserInfo = NULL;
654 }
655 if (pDomainControllerInfo)
656 {
657 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
658 pDomainControllerInfo = NULL;
659 }
660 }
661
662LExit:
663 ReleaseStr(pwzBaseScriptKey);
664 ReleaseStr(pwzScriptKey);
665 ReleaseStr(pwzActionData);
666 ReleaseStr(pwzRollbackData);
667 if (pUserInfo)
668 {
669 ::NetApiBufferFree(static_cast<LPVOID>(pUserInfo));
670 }
671 if (pDomainControllerInfo)
672 {
673 ::NetApiBufferFree(static_cast<LPVOID>(pDomainControllerInfo));
674 }
675
676 return hr;
677}
678
679
680static HRESULT AddUserToList(
681 __inout SCA_USER** ppsuList
682 )
683{
684 HRESULT hr = S_OK;
685 SCA_USER* psu = static_cast<SCA_USER*>(MemAlloc(sizeof(SCA_USER), TRUE));
686 ExitOnNull(psu, hr, E_OUTOFMEMORY, "failed to allocate memory for new user list element");
687
688 psu->psuNext = *ppsuList;
689 *ppsuList = psu;
690
691LExit:
692 return hr;
693}
694
695
696static HRESULT AddGroupToList(
697 __inout SCA_GROUP** ppsgList
698 )
699{
700 HRESULT hr = S_OK;
701 SCA_GROUP* psg = static_cast<SCA_GROUP*>(MemAlloc(sizeof(SCA_GROUP), TRUE));
702 ExitOnNull(psg, hr, E_OUTOFMEMORY, "failed to allocate memory for new group list element");
703
704 psg->psgNext = *ppsgList;
705 *ppsgList = psg;
706
707LExit:
708 return hr;
709}
diff --git a/src/ext/Util/ca/scauser.h b/src/ext/Util/ca/scauser.h
new file mode 100644
index 00000000..a5fd5ea8
--- /dev/null
+++ b/src/ext/Util/ca/scauser.h
@@ -0,0 +1,67 @@
1#pragma once
2// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
3
4
5enum USER_EXISTS
6{
7 USER_EXISTS_YES,
8 USER_EXISTS_NO,
9 USER_EXISTS_INDETERMINATE
10};
11
12// structs
13struct SCA_GROUP
14{
15 WCHAR wzKey[MAX_DARWIN_KEY + 1];
16 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
17
18 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
19 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
20
21 SCA_GROUP *psgNext;
22};
23
24struct SCA_USER
25{
26 WCHAR wzKey[MAX_DARWIN_KEY + 1];
27 WCHAR wzComponent[MAX_DARWIN_KEY + 1];
28 INSTALLSTATE isInstalled;
29 INSTALLSTATE isAction;
30
31 WCHAR wzDomain[MAX_DARWIN_COLUMN + 1];
32 WCHAR wzName[MAX_DARWIN_COLUMN + 1];
33 WCHAR wzPassword[MAX_DARWIN_COLUMN + 1];
34 INT iAttributes;
35
36 SCA_GROUP *psgGroups;
37
38 SCA_USER *psuNext;
39};
40
41
42// prototypes
43HRESULT __stdcall ScaGetUser(
44 __in LPCWSTR wzUser,
45 __out SCA_USER* pscau
46 );
47HRESULT __stdcall ScaGetUserDeferred(
48 __in LPCWSTR wzUser,
49 __in WCA_WRAPQUERY_HANDLE hUserQuery,
50 __out SCA_USER* pscau
51 );
52HRESULT __stdcall ScaGetGroup(
53 __in LPCWSTR wzGroup,
54 __out SCA_GROUP* pscag
55 );
56void ScaUserFreeList(
57 __in SCA_USER* psuList
58 );
59void ScaGroupFreeList(
60 __in SCA_GROUP* psgList
61 );
62HRESULT ScaUserRead(
63 __inout SCA_USER** ppsuList
64 );
65HRESULT ScaUserExecute(
66 __in SCA_USER *psuList
67 );
diff --git a/src/ext/Util/ca/secureobj.cpp b/src/ext/Util/ca/secureobj.cpp
new file mode 100644
index 00000000..72842eb5
--- /dev/null
+++ b/src/ext/Util/ca/secureobj.cpp
@@ -0,0 +1,915 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// structs
6LPCWSTR wzQUERY_SECUREOBJECTS = L"SELECT `Wix4SecureObject`.`Wix4SecureObject`, `Wix4SecureObject`.`Table`, `Wix4SecureObject`.`Domain`, `Wix4SecureObject`.`User`, `Wix4SecureObject`.`Attributes`, "
7 L"`Wix4SecureObject`.`Permission`, `Wix4SecureObject`.`Component_`, `Component`.`Attributes` FROM `Wix4SecureObject`,`Component` WHERE "
8 L"`Wix4SecureObject`.`Component_`=`Component`.`Component`";
9enum eQUERY_SECUREOBJECTS { QSO_SECUREOBJECT = 1, QSO_TABLE, QSO_DOMAIN, QSO_USER, QSO_ATTRIBUTES, QSO_PERMISSION, QSO_COMPONENT, QSO_COMPATTRIBUTES };
10
11LPCWSTR wzQUERY_REGISTRY = L"SELECT `Registry`.`Registry`, `Registry`.`Root`, `Registry`.`Key` FROM `Registry` WHERE `Registry`.`Registry`=?";
12enum eQUERY_OBJECTCOMPONENT { QSOC_REGISTRY = 1, QSOC_REGROOT, QSOC_REGKEY };
13
14LPCWSTR wzQUERY_SERVICEINSTALL = L"SELECT `ServiceInstall`.`Name` FROM `ServiceInstall` WHERE `ServiceInstall`.`ServiceInstall`=?";
15enum eQUERY_SECURESERVICEINSTALL { QSSI_NAME = 1 };
16
17enum eOBJECTTYPE { OT_UNKNOWN, OT_SERVICE, OT_FOLDER, OT_FILE, OT_REGISTRY };
18
19enum eSECURE_OBJECT_ATTRIBUTE
20{
21 SECURE_OBJECT_ATTRIBUTE_INHERITABLE = 0x1,
22};
23
24static eOBJECTTYPE EObjectTypeFromString(
25 __in LPCWSTR pwzTable
26 )
27{
28 if (NULL == pwzTable)
29 {
30 return OT_UNKNOWN;
31 }
32
33 eOBJECTTYPE eType = OT_UNKNOWN;
34
35 // ensure we're looking at a known table
36 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
37 {
38 eType = OT_SERVICE;
39 }
40 else if (0 == lstrcmpW(L"CreateFolder", pwzTable))
41 {
42 eType = OT_FOLDER;
43 }
44 else if (0 == lstrcmpW(L"File", pwzTable))
45 {
46 eType = OT_FILE;
47 }
48 else if (0 == lstrcmpW(L"Registry", pwzTable))
49 {
50 eType = OT_REGISTRY;
51 }
52
53 return eType;
54}
55
56static SE_OBJECT_TYPE SEObjectTypeFromString(
57 __in LPCWSTR pwzTable
58 )
59{
60 if (NULL == pwzTable)
61 {
62 return SE_UNKNOWN_OBJECT_TYPE;
63 }
64
65 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
66
67 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
68 {
69 objectType = SE_SERVICE;
70 }
71 else if (0 == lstrcmpW(L"CreateFolder", pwzTable) || 0 == lstrcmpW(L"File", pwzTable))
72 {
73 objectType = SE_FILE_OBJECT;
74 }
75 else if (0 == lstrcmpW(L"Registry", pwzTable))
76 {
77 objectType = SE_REGISTRY_KEY;
78 }
79 else
80 {
81 // Do nothing; we'll return SE_UNKNOWN_OBJECT_TYPE, and the caller should handle the situation
82 }
83
84 return objectType;
85}
86
87static HRESULT StoreACLRollbackInfo(
88 __in LPWSTR pwzObject,
89 __in LPCWSTR pwzTable
90 )
91{
92 HRESULT hr = S_OK;
93 DWORD er = ERROR_SUCCESS;
94 PSECURITY_DESCRIPTOR psd = NULL;
95 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
96 DWORD dwRevision = 0;
97 LPWSTR pwzCustomActionData = NULL;
98 LPWSTR pwzSecurityInfo = NULL;
99
100 Assert(pwzObject && pwzTable);
101
102 SE_OBJECT_TYPE objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
103
104 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
105 {
106 er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL, &psd);
107 if (ERROR_FILE_NOT_FOUND == er || ERROR_PATH_NOT_FOUND == er || ERROR_SERVICE_DOES_NOT_EXIST == HRESULT_CODE(er))
108 {
109 // If the file, path or service doesn't exist yet, skip rollback without a message
110 hr = HRESULT_FROM_WIN32(er);
111 ExitFunction();
112 }
113
114 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "Unable to schedule rollback for object: %ls", pwzObject);
115
116 //Need to see if DACL is protected so getting Descriptor information
117 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
118 {
119 ExitOnLastError(hr, "Unable to schedule rollback for object (failed to get security descriptor control): %ls", pwzObject);
120 }
121
122 // Convert the security information to a string, and write this to the custom action data
123 if (!::ConvertSecurityDescriptorToStringSecurityDescriptorW(psd,SDDL_REVISION_1,DACL_SECURITY_INFORMATION,&pwzSecurityInfo,NULL))
124 {
125 hr = E_UNEXPECTED;
126 ExitOnFailure(hr, "Unable to schedule rollback for object (failed to convert security descriptor to a valid security descriptor string): %ls", pwzObject);
127 }
128
129 hr = WcaWriteStringToCaData(pwzObject, &pwzCustomActionData);
130 ExitOnFailure(hr, "failed to add object data to rollback CustomActionData");
131
132 hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
133 ExitOnFailure(hr, "failed to add table name to rollback CustomActionData");
134
135 hr = WcaWriteStringToCaData(pwzSecurityInfo, &pwzCustomActionData);
136 ExitOnFailure(hr, "failed to add security info data to rollback CustomActionData");
137
138 // Write a 1 if DACL is protected, 0 otherwise
139 if (sdc & SE_DACL_PROTECTED)
140 {
141 hr = WcaWriteIntegerToCaData(1,&pwzCustomActionData);
142 ExitOnFailure(hr, "failed to add data to rollbackCustomActionData");
143 }
144 else
145 {
146 hr = WcaWriteIntegerToCaData(0,&pwzCustomActionData);
147 ExitOnFailure(hr, "failed to add data to rollback CustomActionData");
148 }
149
150 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecSecureObjectsRollback"), pwzCustomActionData, COST_SECUREOBJECT);
151 ExitOnFailure(hr, "failed to schedule ExecSecureObjectsRollback for item: %ls of type: %ls", pwzObject, pwzTable);
152
153 ReleaseStr(pwzCustomActionData);
154 pwzCustomActionData = NULL;
155
156 }
157 else
158 {
159 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
160 }
161LExit:
162 ReleaseStr(pwzCustomActionData);
163
164 if (psd)
165 {
166 ::LocalFree(psd);
167 }
168
169 return hr;
170}
171
172static HRESULT GetTargetPath(
173 __in eOBJECTTYPE eType,
174 __in LPCWSTR pwzSecureObject,
175 __out LPWSTR* ppwzTargetPath
176 )
177{
178 HRESULT hr = S_OK;
179
180 PMSIHANDLE hView = NULL;
181 PMSIHANDLE hRecObject = NULL;
182 PMSIHANDLE hRec = NULL;
183
184 int iRoot = 0;
185 int iAllUsers = 0;
186 LPWSTR pwzKey = NULL;
187 LPWSTR pwzFormattedString = NULL;
188
189 if (OT_SERVICE == eType)
190 {
191 hr = WcaTableExists(L"ServiceInstall");
192 if (S_FALSE == hr)
193 {
194 hr = E_UNEXPECTED;
195 }
196 ExitOnFailure(hr, "failed to open ServiceInstall table to secure object");
197
198 hr = WcaOpenView(wzQUERY_SERVICEINSTALL, &hView);
199 ExitOnFailure(hr, "failed to open view on ServiceInstall table");
200
201 // create a record that stores the object to secure
202 hRec = MsiCreateRecord(1);
203 MsiRecordSetStringW(hRec, 1, pwzSecureObject);
204
205 // execute a view looking for the object's ServiceInstall.ServiceInstall row.
206 hr = WcaExecuteView(hView, hRec);
207 ExitOnFailure(hr, "failed to execute view on ServiceInstall table");
208 hr = WcaFetchSingleRecord(hView, &hRecObject);
209 ExitOnFailure(hr, "failed to fetch ServiceInstall row for secure object");
210
211 hr = WcaGetRecordFormattedString(hRecObject, QSSI_NAME, ppwzTargetPath);
212 ExitOnFailure(hr, "failed to get service name for secure object: %ls", pwzSecureObject);
213 }
214 else if (OT_FOLDER == eType)
215 {
216 hr = WcaGetTargetPath(pwzSecureObject, ppwzTargetPath);
217 ExitOnFailure(hr, "failed to get target path for directory id: %ls", pwzSecureObject);
218 }
219 else if (OT_FILE == eType)
220 {
221 hr = StrAllocFormatted(&pwzFormattedString, L"[#%s]", pwzSecureObject);
222 ExitOnFailure(hr, "failed to create formatted string for securing file object: %ls", pwzSecureObject);
223
224 hr = WcaGetFormattedString(pwzFormattedString, ppwzTargetPath);
225 ExitOnFailure(hr, "failed to get file path from formatted string: %ls for secure object: %ls", pwzFormattedString, pwzSecureObject);
226 }
227 else if (OT_REGISTRY == eType)
228 {
229 hr = WcaTableExists(L"Registry");
230 if (S_FALSE == hr)
231 {
232 hr = E_UNEXPECTED;
233 }
234 ExitOnFailure(hr, "failed to open Registry table to secure object");
235
236 hr = WcaOpenView(wzQUERY_REGISTRY, &hView);
237 ExitOnFailure(hr, "failed to open view on Registry table");
238
239 // create a record that stores the object to secure
240 hRec = MsiCreateRecord(1);
241 MsiRecordSetStringW(hRec, 1, pwzSecureObject);
242
243 // execute a view looking for the object's Registry row
244 hr = WcaExecuteView(hView, hRec);
245 ExitOnFailure(hr, "failed to execute view on Registry table");
246 hr = WcaFetchSingleRecord(hView, &hRecObject);
247 ExitOnFailure(hr, "failed to fetch Registry row for secure object");
248
249 hr = WcaGetRecordInteger(hRecObject, QSOC_REGROOT, &iRoot);
250 ExitOnFailure(hr, "Failed to get reg key root for secure object: %ls", pwzSecureObject);
251
252 hr = WcaGetRecordFormattedString(hRecObject, QSOC_REGKEY, &pwzKey);
253 ExitOnFailure(hr, "Failed to get reg key for secure object: %ls", pwzSecureObject);
254
255 // Decode the root value
256 if (-1 == iRoot)
257 {
258 // They didn't specify a root so that means it's either HKCU or HKLM depending on ALLUSERS property
259 hr = WcaGetIntProperty(L"ALLUSERS", &iAllUsers);
260 ExitOnFailure(hr, "failed to get value of ALLUSERS property");
261
262 if (1 == iAllUsers)
263 {
264 hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0);
265 ExitOnFailure(hr, "failed to allocate target registry string with HKLM root");
266 }
267 else
268 {
269 hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0);
270 ExitOnFailure(hr, "failed to allocate target registry string with HKCU root");
271 }
272 }
273 else if (msidbRegistryRootClassesRoot == iRoot)
274 {
275 hr = StrAllocString(ppwzTargetPath, L"CLASSES_ROOT\\", 0);
276 ExitOnFailure(hr, "failed to allocate target registry string with HKCR root");
277 }
278 else if (msidbRegistryRootCurrentUser == iRoot)
279 {
280 hr = StrAllocString(ppwzTargetPath, L"CURRENT_USER\\", 0);
281 ExitOnFailure(hr, "failed to allocate target registry string with HKCU root");
282 }
283 else if (msidbRegistryRootLocalMachine == iRoot)
284 {
285 hr = StrAllocString(ppwzTargetPath, L"MACHINE\\", 0);
286 ExitOnFailure(hr, "failed to allocate target registry string with HKLM root");
287 }
288 else if (msidbRegistryRootUsers == iRoot)
289 {
290 hr = StrAllocString(ppwzTargetPath, L"USERS\\", 0);
291 ExitOnFailure(hr, "failed to allocate target registry string with HKU root");
292 }
293 else
294 {
295 ExitOnFailure(hr = E_UNEXPECTED, "Unknown registry key root specified for secure object: '%ls' root: %d", pwzSecureObject, iRoot);
296 }
297
298 hr = StrAllocConcat(ppwzTargetPath, pwzKey, 0);
299 ExitOnFailure(hr, "Failed to concat key: %ls for secure object: %ls", pwzKey, pwzSecureObject);
300 }
301 else
302 {
303 AssertSz(FALSE, "How did you get here?");
304 ExitOnFailure(hr = E_UNEXPECTED, "Unknown secure object type: %d", eType);
305 }
306
307LExit:
308 ReleaseStr(pwzFormattedString);
309 ReleaseStr(pwzKey);
310
311 return hr;
312}
313
314/******************************************************************
315 SchedSecureObjects - entry point for SchedSecureObjects Custom Action
316
317 called as Type 1 CustomAction (binary DLL) from Windows Installer
318 in InstallExecuteSequence, to schedule ExecSecureObjects
319******************************************************************/
320extern "C" UINT __stdcall SchedSecureObjects(
321 __in MSIHANDLE hInstall
322 )
323{
324// AssertSz(FALSE, "debug SchedSecureObjects");
325 HRESULT hr = S_OK;
326 UINT er = ERROR_SUCCESS;
327
328 LPWSTR pwzSecureObject = NULL;
329 LPWSTR pwzData = NULL;
330 LPWSTR pwzTable = NULL;
331 LPWSTR pwzTargetPath = NULL;
332
333 PMSIHANDLE hView = NULL;
334 PMSIHANDLE hRec = NULL;
335
336 INSTALLSTATE isInstalled;
337 INSTALLSTATE isAction;
338
339 LPWSTR pwzCustomActionData = NULL;
340
341 DWORD cObjects = 0;
342 eOBJECTTYPE eType = OT_UNKNOWN;
343 DWORD dwAttributes = 0;
344
345 //
346 // initialize
347 //
348 hr = WcaInitialize(hInstall, "SchedSecureObjects");
349 ExitOnFailure(hr, "failed to initialize");
350
351 // anything to do?
352 if (S_OK != WcaTableExists(L"Wix4SecureObject"))
353 {
354 WcaLog(LOGMSG_STANDARD, "Wix4SecureObject table doesn't exist, so there are no objects to secure.");
355 ExitFunction();
356 }
357
358 //
359 // loop through all the objects to be secured
360 //
361 hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView);
362 ExitOnFailure(hr, "failed to open view on Wix4SecureObject table");
363 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
364 {
365 hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable);
366 ExitOnFailure(hr, "failed to get object table");
367
368 eType = EObjectTypeFromString(pwzTable);
369
370 if (OT_UNKNOWN == eType)
371 {
372 ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable);
373 }
374
375 int iCompAttributes = 0;
376 hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes);
377 ExitOnFailure(hr, "failed to get Component attributes for secure object");
378
379 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
380
381 // Only process entries in the Wix4SecureObject table whose components match the bitness of this CA
382#ifdef _WIN64
383 if (!fIs64Bit)
384 {
385 continue;
386 }
387#else
388 if (fIs64Bit)
389 {
390 continue;
391 }
392#endif
393
394 // Get the object to secure
395 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject);
396 ExitOnFailure(hr, "failed to get name of object");
397
398 hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath);
399 ExitOnFailure(hr, "failed to get target path of object '%ls'", pwzSecureObject);
400
401 hr = WcaGetRecordString(hRec, QSO_COMPONENT, &pwzData);
402 ExitOnFailure(hr, "failed to get Component name for secure object");
403
404 //
405 // if we are installing this Component
406 //
407 er = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
408 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for Component: %ls", pwzData);
409
410 if (WcaIsInstalling(isInstalled, isAction))
411 {
412 hr = WcaWriteStringToCaData(pwzTargetPath, &pwzCustomActionData);
413 ExitOnFailure(hr, "failed to add data to CustomActionData");
414
415 // add the data to the CustomActionData
416 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzData);
417 ExitOnFailure(hr, "failed to get name of object");
418 hr = WcaWriteStringToCaData(pwzTable, &pwzCustomActionData);
419 ExitOnFailure(hr, "failed to add data to CustomActionData");
420
421 hr = WcaGetRecordFormattedString(hRec, QSO_DOMAIN, &pwzData);
422 ExitOnFailure(hr, "failed to get domain for user to configure object");
423 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
424 ExitOnFailure(hr, "failed to add data to CustomActionData");
425
426 hr = WcaGetRecordFormattedString(hRec, QSO_USER, &pwzData);
427 ExitOnFailure(hr, "failed to get user to configure object");
428 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
429 ExitOnFailure(hr, "failed to add data to CustomActionData");
430
431 hr = WcaGetRecordInteger(hRec, QSO_ATTRIBUTES, reinterpret_cast<int*>(&dwAttributes));
432 ExitOnFailure(hr, "failed to get attributes to configure object");
433 hr = WcaWriteIntegerToCaData(dwAttributes, &pwzCustomActionData);
434 ExitOnFailure(hr, "failed to add data to CustomActionData");
435
436 hr = WcaGetRecordString(hRec, QSO_PERMISSION, &pwzData);
437 ExitOnFailure(hr, "failed to get permission to configure object");
438 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
439 ExitOnFailure(hr, "failed to add data to CustomActionData");
440
441 ++cObjects;
442 }
443 }
444
445 // if we looped through all records all is well
446 if (E_NOMOREITEMS == hr)
447 hr = S_OK;
448 ExitOnFailure(hr, "failed while looping through all objects to secure");
449
450 //
451 // schedule the custom action and add to progress bar
452 //
453 if (pwzCustomActionData && *pwzCustomActionData)
454 {
455 Assert(0 < cObjects);
456
457 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecSecureObjects"), pwzCustomActionData, cObjects * COST_SECUREOBJECT);
458 ExitOnFailure(hr, "failed to schedule ExecSecureObjects action");
459 }
460
461LExit:
462 ReleaseStr(pwzSecureObject);
463 ReleaseStr(pwzCustomActionData);
464 ReleaseStr(pwzData);
465 ReleaseStr(pwzTable);
466 ReleaseStr(pwzTargetPath);
467
468 if (FAILED(hr))
469 {
470 er = ERROR_INSTALL_FAILURE;
471 }
472 return WcaFinalize(er);
473}
474
475/******************************************************************
476 SchedSecureObjectsRollback - entry point for SchedSecureObjectsRollback Custom Action
477
478 called as Type 1 CustomAction (binary DLL) from Windows Installer
479 in InstallExecuteSequence before SchedSecureObjects
480******************************************************************/
481extern "C" UINT __stdcall SchedSecureObjectsRollback(
482 __in MSIHANDLE hInstall
483 )
484{
485// AssertSz(FALSE, "debug SchedSecureObjectsRollback");
486 HRESULT hr = S_OK;
487 UINT er = ERROR_SUCCESS;
488
489 LPWSTR pwzSecureObject = NULL;
490 LPWSTR pwzTable = NULL;
491 LPWSTR pwzTargetPath = NULL;
492
493 PMSIHANDLE hView = NULL;
494 PMSIHANDLE hRec = NULL;
495
496 LPWSTR pwzCustomActionData = NULL;
497
498 eOBJECTTYPE eType = OT_UNKNOWN;
499
500 //
501 // initialize
502 //
503 hr = WcaInitialize(hInstall, "SchedSecureObjectsRollback");
504 ExitOnFailure(hr, "failed to initialize");
505
506 //
507 // loop through all the objects to be secured
508 //
509 hr = WcaOpenExecuteView(wzQUERY_SECUREOBJECTS, &hView);
510 ExitOnFailure(hr, "failed to open view on Wix4SecureObject table");
511 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
512 {
513 hr = WcaGetRecordString(hRec, QSO_TABLE, &pwzTable);
514 ExitOnFailure(hr, "failed to get object table");
515
516 eType = EObjectTypeFromString(pwzTable);
517
518 if (OT_UNKNOWN == eType)
519 {
520 ExitOnFailure(hr = E_INVALIDARG, "unknown SecureObject.Table: %ls", pwzTable);
521 }
522
523 int iCompAttributes = 0;
524 hr = WcaGetRecordInteger(hRec, QSO_COMPATTRIBUTES, &iCompAttributes);
525 ExitOnFailure(hr, "failed to get Component attributes for secure object");
526
527 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
528
529 // Only process entries in the Wix4SecureObject table whose components match the bitness of this CA
530#ifdef _WIN64
531 if (!fIs64Bit)
532 {
533 continue;
534 }
535#else
536 if (fIs64Bit)
537 {
538 continue;
539 }
540#endif
541
542 // get the object being secured that we are planning to schedule rollback for
543 hr = WcaGetRecordString(hRec, QSO_SECUREOBJECT, &pwzSecureObject);
544 ExitOnFailure(hr, "failed to get name of object");
545
546 hr = GetTargetPath(eType, pwzSecureObject, &pwzTargetPath);
547 ExitOnFailure(hr, "failed to get target path of object '%ls' in order to schedule rollback", pwzSecureObject);
548
549 hr = StoreACLRollbackInfo(pwzTargetPath, pwzTable);
550 if (FAILED(hr))
551 {
552 WcaLog(LOGMSG_STANDARD, "Failed to store ACL rollback information with error 0x%x - continuing", hr);
553 }
554 }
555
556 // if we looped through all records all is well
557 if (E_NOMOREITEMS == hr)
558 {
559 hr = S_OK;
560 }
561 ExitOnFailure(hr, "failed while looping through all objects to schedule rollback for");
562
563LExit:
564 ReleaseStr(pwzCustomActionData);
565 ReleaseStr(pwzSecureObject);
566 ReleaseStr(pwzTable);
567 ReleaseStr(pwzTargetPath);
568
569 if (FAILED(hr))
570 {
571 er = ERROR_INSTALL_FAILURE;
572 }
573 return WcaFinalize(er);
574}
575
576/******************************************************************
577 CaExecSecureObjects - entry point for SecureObjects Custom Action
578 called as Type 1025 CustomAction (deferred binary DLL)
579
580 NOTE: deferred CustomAction since it modifies the machine
581 NOTE: CustomActionData == wzObject\twzTable\twzDomain\twzUser\tdwAttributes\tdwPermissions\t...
582******************************************************************/
583extern "C" UINT __stdcall ExecSecureObjects(
584 __in MSIHANDLE hInstall
585 )
586{
587// AssertSz(FALSE, "debug ExecSecureObjects");
588 HRESULT hr = S_OK;
589 DWORD er = ERROR_SUCCESS;
590
591 LPWSTR pwz = NULL;
592 LPWSTR pwzData = NULL;
593 LPWSTR pwzObject = NULL;
594 LPWSTR pwzTable = NULL;
595 LPWSTR pwzDomain = NULL;
596 DWORD dwRevision = 0;
597 LPWSTR pwzUser = NULL;
598 DWORD dwPermissions = 0;
599 DWORD dwAttributes = 0;
600 LPWSTR pwzAccount = NULL;
601 PSID psid = NULL;
602
603 EXPLICIT_ACCESSW ea = {0};
604 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
605 PSECURITY_DESCRIPTOR psd = NULL;
606 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
607 SECURITY_INFORMATION si = {0};
608 PACL pAclExisting = NULL; // doesn't get freed
609 PACL pAclNew = NULL;
610
611 PMSIHANDLE hActionRec = ::MsiCreateRecord(1);
612
613 //
614 // initialize
615 //
616 hr = WcaInitialize(hInstall, "ExecSecureObjects");
617 ExitOnFailure(hr, "failed to initialize");
618
619 hr = WcaGetProperty(L"CustomActionData", &pwzData);
620 ExitOnFailure(hr, "failed to get CustomActionData");
621
622 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
623
624 pwz = pwzData;
625
626 //
627 // loop through all the passed in data
628 //
629 while (pwz && *pwz)
630 {
631 hr = WcaReadStringFromCaData(&pwz, &pwzObject);
632 ExitOnFailure(hr, "failed to process CustomActionData");
633
634 hr = WcaReadStringFromCaData(&pwz, &pwzTable);
635 ExitOnFailure(hr, "failed to process CustomActionData");
636 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
637 ExitOnFailure(hr, "failed to process CustomActionData");
638 hr = WcaReadStringFromCaData(&pwz, &pwzUser);
639 ExitOnFailure(hr, "failed to process CustomActionData");
640 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwAttributes));
641 ExitOnFailure(hr, "failed to process CustomActionData");
642 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwPermissions));
643 ExitOnFailure(hr, "failed to process CustomActionData");
644
645 WcaLog(LOGMSG_VERBOSE, "Securing Object: %ls Type: %ls User: %ls", pwzObject, pwzTable, pwzUser);
646
647 //
648 // create the appropriate SID
649 //
650
651 // figure out the right user to put into the access block
652 if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Everyone"))
653 {
654 hr = AclGetWellKnownSid(WinWorldSid, &psid);
655 }
656 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Administrators"))
657 {
658 hr = AclGetWellKnownSid(WinBuiltinAdministratorsSid, &psid);
659 }
660 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalSystem"))
661 {
662 hr = AclGetWellKnownSid(WinLocalSystemSid, &psid);
663 }
664 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"LocalService"))
665 {
666 hr = AclGetWellKnownSid(WinLocalServiceSid, &psid);
667 }
668 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"NetworkService"))
669 {
670 hr = AclGetWellKnownSid(WinNetworkServiceSid, &psid);
671 }
672 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"AuthenticatedUser"))
673 {
674 hr = AclGetWellKnownSid(WinAuthenticatedUserSid, &psid);
675 }
676 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Guests"))
677 {
678 hr = AclGetWellKnownSid(WinBuiltinGuestsSid, &psid);
679 }
680 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"CREATOR OWNER"))
681 {
682 hr = AclGetWellKnownSid(WinCreatorOwnerSid, &psid);
683 }
684 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"INTERACTIVE"))
685 {
686 hr = AclGetWellKnownSid(WinInteractiveSid, &psid);
687 }
688 else if (!*pwzDomain && 0 == lstrcmpW(pwzUser, L"Users"))
689 {
690 hr = AclGetWellKnownSid(WinBuiltinUsersSid, &psid);
691 }
692 else
693 {
694 hr = StrAllocFormatted(&pwzAccount, L"%s%s%s", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);
695 ExitOnFailure(hr, "failed to build domain user name");
696
697 hr = AclGetAccountSid(NULL, pwzAccount, &psid);
698 }
699 ExitOnFailure(hr, "failed to get sid for account: %ls%ls%ls", pwzDomain, *pwzDomain ? L"\\" : L"", pwzUser);
700
701 //
702 // build up the explicit access
703 //
704 ea.grfAccessMode = SET_ACCESS;
705
706 if (dwAttributes & SECURE_OBJECT_ATTRIBUTE_INHERITABLE)
707 {
708 ea.grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT;
709 }
710 else
711 {
712 ea.grfInheritance = NO_INHERITANCE;
713 }
714
715#pragma prefast(push)
716#pragma prefast(disable:25029)
717 ::BuildTrusteeWithSidW(&ea.Trustee, psid);
718#pragma prefast(pop)
719
720 objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
721
722 // always add these permissions for services
723 // these are basic permissions that are often forgotten
724 if (0 == lstrcmpW(L"ServiceInstall", pwzTable))
725 {
726 dwPermissions |= SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS | SERVICE_ENUMERATE_DEPENDENTS | SERVICE_INTERROGATE;
727 }
728
729 ea.grfAccessPermissions = dwPermissions;
730
731 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
732 {
733 er = ::GetNamedSecurityInfoW(pwzObject, objectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pAclExisting, NULL, &psd);
734 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get security info for object: %ls", pwzObject);
735
736 //Need to see if DACL is protected so getting Descriptor information
737 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
738 {
739 ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject);
740 }
741
742#pragma prefast(push)
743#pragma prefast(disable:25029)
744 er = ::SetEntriesInAclW(1, &ea, pAclExisting, &pAclNew);
745#pragma prefast(pop)
746 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to add ACLs for object: %ls", pwzObject);
747
748 if (sdc & SE_DACL_PROTECTED)
749 {
750 si = DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION;
751 }
752 else
753 {
754 si = DACL_SECURITY_INFORMATION;
755 }
756 er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pAclNew, NULL);
757 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrSecureObjectsFailedSet, "failed to set security info for object: %ls", pwzObject);
758 }
759 else
760 {
761 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
762 }
763
764 hr = WcaProgressMessage(COST_SECUREOBJECT, FALSE);
765 ExitOnFailure(hr, "failed to send progress message");
766
767 objectType = SE_UNKNOWN_OBJECT_TYPE;
768 }
769
770LExit:
771 ReleaseStr(pwzUser);
772 ReleaseStr(pwzDomain);
773 ReleaseStr(pwzTable);
774 ReleaseStr(pwzObject);
775 ReleaseStr(pwzData);
776 ReleaseStr(pwzAccount);
777
778 if (pAclNew)
779 {
780 ::LocalFree(pAclNew);
781 }
782 if (psd)
783 {
784 ::LocalFree(psd);
785 }
786 if (psid)
787 {
788 AclFreeSid(psid);
789 }
790
791 if (FAILED(hr))
792 {
793 er = ERROR_INSTALL_FAILURE;
794 }
795 return WcaFinalize(er);
796}
797
798extern "C" UINT __stdcall ExecSecureObjectsRollback(
799 __in MSIHANDLE hInstall
800 )
801{
802// AssertSz(FALSE, "debug ExecSecureObjectsRollback");
803 HRESULT hr = S_OK;
804 DWORD er = ERROR_SUCCESS;
805
806 LPWSTR pwz = NULL;
807 LPWSTR pwzData = NULL;
808 LPWSTR pwzObject = NULL;
809 LPWSTR pwzTable = NULL;
810 LPWSTR pwzSecurityInfo = NULL;
811
812 SE_OBJECT_TYPE objectType = SE_UNKNOWN_OBJECT_TYPE;
813 PSECURITY_DESCRIPTOR psd = NULL;
814 ULONG psdSize;
815 SECURITY_DESCRIPTOR_CONTROL sdc = {0};
816 SECURITY_INFORMATION si = DACL_SECURITY_INFORMATION;
817 PACL pDacl = NULL;
818 BOOL bDaclPresent = false;
819 BOOL bDaclDefaulted = false;
820 DWORD dwRevision = 0;
821 int iProtected;
822
823 // initialize
824 hr = WcaInitialize(hInstall, "ExecSecureObjectsRollback");
825 ExitOnFailure(hr, "failed to initialize");
826
827 hr = WcaGetProperty(L"CustomActionData", &pwzData);
828 ExitOnFailure(hr, "failed to get CustomActionData");
829
830 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
831
832 pwz = pwzData;
833
834 hr = WcaReadStringFromCaData(&pwz, &pwzObject);
835 ExitOnFailure(hr, "failed to process CustomActionData");
836
837 hr = WcaReadStringFromCaData(&pwz, &pwzTable);
838 ExitOnFailure(hr, "failed to process CustomActionData");
839
840 objectType = SEObjectTypeFromString(const_cast<LPCWSTR> (pwzTable));
841
842 if (SE_UNKNOWN_OBJECT_TYPE != objectType)
843 {
844 hr = WcaReadStringFromCaData(&pwz, &pwzSecurityInfo);
845 ExitOnFailure(hr, "failed to process CustomActionData");
846
847 hr = WcaReadIntegerFromCaData(&pwz, &iProtected);
848 ExitOnFailure(hr, "failed to process CustomActionData");
849
850 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(pwzSecurityInfo,SDDL_REVISION_1,&psd,&psdSize))
851 {
852 ExitOnLastError(hr, "failed to convert security descriptor string to a valid security descriptor");
853 }
854
855 if (!::GetSecurityDescriptorDacl(psd,&bDaclPresent,&pDacl,&bDaclDefaulted))
856 {
857 hr = E_UNEXPECTED;
858 ExitOnFailure(hr, "failed to get security descriptor's DACL - error code: %d",pwzSecurityInfo,GetLastError());
859 }
860
861 // The below situation may always be caught by the above if block - the documentation isn't very clear. To be safe, we're going to test for it.
862 if (!bDaclPresent)
863 {
864 hr = E_UNEXPECTED;
865 ExitOnFailure(hr, "security descriptor does not contain a DACL");
866 }
867
868 //Need to see if DACL is protected so getting Descriptor information
869 if (!::GetSecurityDescriptorControl(psd, &sdc, &dwRevision))
870 {
871 ExitOnLastError(hr, "failed to get security descriptor control for object: %ls", pwzObject);
872 }
873
874 // Write a 1 if DACL is protected, 0 otherwise
875 switch (iProtected)
876 {
877 case 0:
878 // Unnecessary to do anything - leave si to the default flags
879 break;
880
881 case 1:
882 si = si | PROTECTED_DACL_SECURITY_INFORMATION;
883 break;
884
885 default:
886 hr = E_UNEXPECTED;
887 ExitOnFailure(hr, "unrecognized value in CustomActionData");
888 break;
889 }
890
891 er = ::SetNamedSecurityInfoW(pwzObject, objectType, si, NULL, NULL, pDacl, NULL);
892 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to set security info for object: %ls error code: %d", pwzObject, GetLastError());
893 }
894 else
895 {
896 MessageExitOnFailure(hr = E_UNEXPECTED, msierrSecureObjectsUnknownType, "unknown object type: %ls", pwzTable);
897 }
898
899LExit:
900 ReleaseStr(pwzData);
901 ReleaseStr(pwzObject);
902 ReleaseStr(pwzTable);
903 ReleaseStr(pwzSecurityInfo);
904
905 if (psd)
906 {
907 ::LocalFree(psd);
908 }
909
910 if (FAILED(hr))
911 {
912 er = ERROR_INSTALL_FAILURE;
913 }
914 return WcaFinalize(er);
915}
diff --git a/src/ext/Util/ca/serviceconfig.cpp b/src/ext/Util/ca/serviceconfig.cpp
new file mode 100644
index 00000000..04b25ffa
--- /dev/null
+++ b/src/ext/Util/ca/serviceconfig.cpp
@@ -0,0 +1,821 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5// structs
6LPCWSTR wzQUERY_SERVICECONFIG = L"SELECT `ServiceName`, `Component_`, `NewService`, `FirstFailureActionType`, `SecondFailureActionType`, `ThirdFailureActionType`, `ResetPeriodInDays`, `RestartServiceDelayInSeconds`, `ProgramCommandLine`, `RebootMessage` FROM `Wix4ServiceConfig`";
7enum eQUERY_SERVICECONFIG { QSC_SERVICENAME = 1, QSC_COMPONENT, QSC_NEWSERVICE, QSC_FIRSTFAILUREACTIONTYPE, QSC_SECONDFAILUREACTIONTYPE, QSC_THIRDFAILUREACTIONTYPE, QSC_RESETPERIODINDAYS, QSC_RESTARTSERVICEDELAYINSECONDS, QSC_PROGRAMCOMMANDLINE, QSC_REBOOTMESSAGE };
8
9// consts
10LPCWSTR c_wzActionTypeNone = L"none";
11LPCWSTR c_wzActionTypeReboot = L"reboot";
12LPCWSTR c_wzActionTypeRestart = L"restart";
13LPCWSTR c_wzActionTypeRunCommand = L"runCommand";
14
15// prototypes
16static SC_ACTION_TYPE GetSCActionType(
17 __in LPCWSTR pwzActionTypeName
18 );
19
20static HRESULT GetSCActionTypeString(
21 __in SC_ACTION_TYPE type,
22 __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
23 __in DWORD cchActionTypeString
24 );
25
26static HRESULT GetService(
27 __in SC_HANDLE hSCM,
28 __in LPCWSTR wzService,
29 __in DWORD dwOpenServiceAccess,
30 __out SC_HANDLE* phService
31 );
32
33static HRESULT ConfigureService(
34 __in SC_HANDLE hSCM,
35 __in SC_HANDLE hService,
36 __in LPCWSTR wzServiceName,
37 __in DWORD dwRestartServiceDelayInSeconds,
38 __in LPCWSTR wzFirstFailureActionType,
39 __in LPCWSTR wzSecondFailureActionType,
40 __in LPCWSTR wzThirdFailureActionType,
41 __in DWORD dwResetPeriodInDays,
42 __in LPWSTR wzRebootMessage,
43 __in LPWSTR wzProgramCommandLine
44 );
45
46
47/******************************************************************
48SchedServiceConfig - entry point for SchedServiceConfig Custom Action
49
50called as Type 1 CustomAction (binary DLL) from Windows Installer
51in InstallExecuteSequence before CaExecServiceConfig
52********************************************************************/
53extern "C" UINT __stdcall SchedServiceConfig(
54 __in MSIHANDLE hInstall
55 )
56{
57 //AssertSz(FALSE, "debug SchedServiceConfig");
58 HRESULT hr = S_OK;
59 UINT er = ERROR_SUCCESS;
60
61 LPWSTR pwzScriptKey = NULL;
62 LPWSTR pwzCustomActionData = NULL;
63
64 PMSIHANDLE hView = NULL;
65 PMSIHANDLE hRec = NULL;
66 LPWSTR pwzData = NULL;
67 int iData = 0;
68 DWORD cServices = 0;
69
70 // initialize
71 hr = WcaInitialize(hInstall, "SchedServiceConfig");
72 ExitOnFailure(hr, "Failed to initialize.");
73
74 // Get the script key for this CustomAction and put it on the front of the
75 // CustomActionData of the install action.
76 hr = WcaCaScriptCreateKey(&pwzScriptKey);
77 ExitOnFailure(hr, "Failed to get encoding key.");
78
79 hr = WcaWriteStringToCaData(pwzScriptKey, &pwzCustomActionData);
80 ExitOnFailure(hr, "Failed to add encoding key to CustomActionData.");
81
82 // Loop through all the services to be configured.
83 hr = WcaOpenExecuteView(wzQUERY_SERVICECONFIG, &hView);
84 ExitOnFailure(hr, "Failed to open view on Wix4ServiceConfig table.");
85
86 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
87 {
88 INSTALLSTATE isInstalled = INSTALLSTATE_UNKNOWN;
89 INSTALLSTATE isAction = INSTALLSTATE_UNKNOWN;
90
91 // Get component name to check if we are installing it. If so
92 // then add the table data to the CustomActionData, otherwise
93 // skip it.
94 hr = WcaGetRecordString(hRec, QSC_COMPONENT, &pwzData);
95 ExitOnFailure(hr, "Failed to get component name");
96
97 hr = ::MsiGetComponentStateW(hInstall, pwzData, &isInstalled, &isAction);
98 ExitOnFailure(hr = HRESULT_FROM_WIN32(hr), "Failed to get install state for Component: %ls", pwzData);
99
100 if (WcaIsInstalling(isInstalled, isAction))
101 {
102 // Add the data to the CustomActionData (for install).
103 hr = WcaGetRecordFormattedString(hRec, QSC_SERVICENAME, &pwzData);
104 ExitOnFailure(hr, "Failed to get name of service.");
105 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
106 ExitOnFailure(hr, "Failed to add name to CustomActionData.");
107
108 hr = WcaGetRecordInteger(hRec, QSC_NEWSERVICE, &iData);
109 ExitOnFailure(hr, "Failed to get Wix4ServiceConfig.NewService.");
110 hr = WcaWriteIntegerToCaData(0 != iData, &pwzCustomActionData);
111 ExitOnFailure(hr, "Failed to add NewService data to CustomActionData");
112
113 hr = WcaGetRecordString(hRec, QSC_FIRSTFAILUREACTIONTYPE, &pwzData);
114 ExitOnFailure(hr, "failed to get first failure action type");
115 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
116 ExitOnFailure(hr, "failed to add data to CustomActionData");
117
118 hr = WcaGetRecordString(hRec, QSC_SECONDFAILUREACTIONTYPE, &pwzData);
119 ExitOnFailure(hr, "failed to get second failure action type");
120 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
121 ExitOnFailure(hr, "failed to add data to CustomActionData");
122
123 hr = WcaGetRecordString(hRec, QSC_THIRDFAILUREACTIONTYPE, &pwzData);
124 ExitOnFailure(hr, "failed to get third failure action type");
125 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
126 ExitOnFailure(hr, "failed to add data to CustomActionData");
127
128 hr = WcaGetRecordInteger(hRec, QSC_RESETPERIODINDAYS, &iData);
129 if (S_FALSE == hr) // deal w/ possible null value
130 {
131 iData = 0;
132 }
133 ExitOnFailure(hr, "failed to get reset period in days between service restart attempts.");
134 hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData);
135 ExitOnFailure(hr, "failed to add data to CustomActionData");
136
137 hr = WcaGetRecordInteger(hRec, QSC_RESTARTSERVICEDELAYINSECONDS, &iData);
138 if (S_FALSE == hr) // deal w/ possible null value
139 {
140 iData = 0;
141 }
142 ExitOnFailure(hr, "failed to get server restart delay value.");
143 hr = WcaWriteIntegerToCaData(iData, &pwzCustomActionData);
144 ExitOnFailure(hr, "failed to add data to CustomActionData");
145
146 hr = WcaGetRecordFormattedString(hRec, QSC_PROGRAMCOMMANDLINE, &pwzData); // null value already dealt w/ properly
147 ExitOnFailure(hr, "failed to get command line to run on service failure.");
148 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
149 ExitOnFailure(hr, "failed to add data to CustomActionData");
150
151 hr = WcaGetRecordString(hRec, QSC_REBOOTMESSAGE, &pwzData); // null value already dealt w/ properly
152 ExitOnFailure(hr, "failed to get message to send to users when server reboots due to service failure.");
153 hr = WcaWriteStringToCaData(pwzData, &pwzCustomActionData);
154 ExitOnFailure(hr, "failed to add data to CustomActionData");
155
156 ++cServices;
157 }
158 }
159
160 // if we looped through all records all is well
161 if (E_NOMOREITEMS == hr)
162 {
163 hr = S_OK;
164 }
165 ExitOnFailure(hr, "failed while looping through all objects to secure");
166
167 // setup CustomActionData and add to progress bar for download
168 if (0 < cServices)
169 {
170 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackServiceConfig"), pwzScriptKey, cServices * COST_SERVICECONFIG);
171 ExitOnFailure(hr, "failed to schedule RollbackServiceConfig action");
172
173 hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecServiceConfig"), pwzCustomActionData, cServices * COST_SERVICECONFIG);
174 ExitOnFailure(hr, "failed to schedule ExecServiceConfig action");
175 }
176
177LExit:
178 ReleaseStr(pwzData);
179 ReleaseStr(pwzCustomActionData);
180 ReleaseStr(pwzScriptKey);
181
182 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
183 return WcaFinalize(er);
184}
185
186
187/******************************************************************
188CaExecServiceConfig - entry point for ServiceConfig Custom Action.
189
190NOTE: deferred CustomAction since it modifies the machine
191NOTE: CustomActionData == wzServiceName\tfNewService\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\tfNewService\t...
192*******************************************************************/
193extern "C" UINT __stdcall ExecServiceConfig(
194 __in MSIHANDLE hInstall
195 )
196{
197 //AssertSz(FALSE, "debug ExecServiceConfig");
198 HRESULT hr = S_OK;
199 DWORD er = 0;
200
201 LPWSTR pwzCustomActionData = NULL;
202 LPWSTR pwz = NULL;
203
204 LPWSTR pwzScriptKey = NULL;
205 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
206
207 LPWSTR pwzServiceName = NULL;
208 BOOL fNewService = FALSE;
209 LPWSTR pwzFirstFailureActionType = NULL;
210 LPWSTR pwzSecondFailureActionType = NULL;
211 LPWSTR pwzThirdFailureActionType = NULL;
212 LPWSTR pwzProgramCommandLine = NULL;
213 LPWSTR pwzRebootMessage = NULL;
214 DWORD dwResetPeriodInDays = 0;
215 DWORD dwRestartServiceDelayInSeconds = 0;
216
217 LPVOID lpMsgBuf = NULL;
218 SC_HANDLE hSCM = NULL;
219 SC_HANDLE hService = NULL;
220
221 DWORD dwRestartDelay = 0;
222 WCHAR wzActionName[32] = { 0 };
223
224 DWORD cbExistingServiceConfig = 0;
225
226 SERVICE_FAILURE_ACTIONSW* psfa = NULL;
227
228 // initialize
229 hr = WcaInitialize(hInstall, "ExecServiceConfig");
230 ExitOnFailure(hr, "failed to initialize");
231
232 // Open the Services Control Manager up front.
233 hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
234 if (NULL == hSCM)
235 {
236 er = ::GetLastError();
237 hr = HRESULT_FROM_WIN32(er);
238
239#pragma prefast(push)
240#pragma prefast(disable:25028)
241 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
242#pragma prefast(pop)
243
244 ExitOnFailure(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf);
245 }
246
247 // First, get the script key out of the CustomActionData and
248 // use that to create the rollback script for this action.
249 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
250 ExitOnFailure(hr, "failed to get CustomActionData");
251
252 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
253
254 pwz = pwzCustomActionData;
255
256 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
257 if (!pwzScriptKey)
258 {
259 hr = E_UNEXPECTED;
260 ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed.");
261 }
262 ExitOnFailure(hr, "Failed to read encoding key from CustomActionData.");
263
264 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
265 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
266
267 // Next, loop through the rest of the CustomActionData, processing
268 // each service config row in turn.
269 while (pwz && *pwz)
270 {
271 hr = WcaReadStringFromCaData(&pwz, &pwzServiceName);
272 ExitOnFailure(hr, "failed to process CustomActionData");
273 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&fNewService));
274 ExitOnFailure(hr, "failed to process CustomActionData");
275 hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType);
276 ExitOnFailure(hr, "failed to process CustomActionData");
277 hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType);
278 ExitOnFailure(hr, "failed to process CustomActionData");
279 hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType);
280 ExitOnFailure(hr, "failed to process CustomActionData");
281 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays));
282 ExitOnFailure(hr, "failed to process CustomActionData");
283 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds));
284 ExitOnFailure(hr, "failed to process CustomActionData");
285 hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine);
286 ExitOnFailure(hr, "failed to process CustomActionData");
287 hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage);
288 ExitOnFailure(hr, "failed to process CustomActionData");
289
290 WcaLog(LOGMSG_VERBOSE, "Configuring Service: %ls", pwzServiceName);
291
292 // Open the handle with all the permissions we might need:
293 // SERVICE_QUERY_CONFIG is needed for QueryServiceConfig2().
294 // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2().
295 // SERVICE_START is required in order to handle SC_ACTION_RESTART action.
296 hr = GetService(hSCM, pwzServiceName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG | SERVICE_START, &hService);
297 ExitOnFailure(hr, "Failed to get service: %ls", pwzServiceName);
298
299 // If we are configuring a service that existed on the machine, we need to
300 // read the existing service configuration and write it out to the rollback
301 // log so rollback can put it back if anything goes wrong.
302 if (!fNewService)
303 {
304 // First, read the existing service config.
305 if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &cbExistingServiceConfig) && ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
306 {
307 ExitWithLastError(hr, "Failed to get current service config info.");
308 }
309
310 psfa = static_cast<LPSERVICE_FAILURE_ACTIONSW>(MemAlloc(cbExistingServiceConfig, TRUE));
311 ExitOnNull(psfa, hr, E_OUTOFMEMORY, "failed to allocate memory for service failure actions.");
312
313 if (!::QueryServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)psfa, cbExistingServiceConfig, &cbExistingServiceConfig))
314 {
315 ExitOnLastError(hr, "failed to Query Service.");
316 }
317
318 // Build up rollback log so we can restore service state if necessary
319 hr = WcaCaScriptWriteString(hRollbackScript, pwzServiceName);
320 ExitOnFailure(hr, "Failed to add service name to Rollback Log");
321
322 // If this service struct is empty, fill in default values
323 if (3 > psfa->cActions)
324 {
325 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
326 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
327
328 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
329 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
330
331 hr = WcaCaScriptWriteString(hRollbackScript, c_wzActionTypeNone);
332 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
333 }
334 else
335 {
336 // psfa actually had actions defined, so use the first three.
337 for (int i = 0; i < 3; ++i)
338 {
339 hr = GetSCActionTypeString(psfa->lpsaActions[i].Type, wzActionName, countof(wzActionName));
340 ExitOnFailure(hr, "failed to query SFA object");
341
342 if (SC_ACTION_RESTART == psfa->lpsaActions[i].Type)
343 {
344 dwRestartDelay = psfa->lpsaActions[i].Delay / 1000;
345 }
346
347 hr = WcaCaScriptWriteString(hRollbackScript, wzActionName);
348 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
349 }
350 }
351
352 hr = WcaCaScriptWriteNumber(hRollbackScript, psfa->dwResetPeriod / (24 * 60 * 60));
353 ExitOnFailure(hr, "failed to add data to CustomActionData");
354
355 hr = WcaCaScriptWriteNumber(hRollbackScript, dwRestartDelay);
356 ExitOnFailure(hr, "failed to add data to CustomActionData");
357
358 // Handle the null cases.
359 if (!psfa->lpCommand)
360 {
361 psfa->lpCommand = L"";
362 }
363 hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpCommand);
364 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
365
366 // Handle the null cases.
367 if (!psfa->lpRebootMsg)
368 {
369 psfa->lpRebootMsg = L"";
370 }
371 hr = WcaCaScriptWriteString(hRollbackScript, psfa->lpRebootMsg);
372 ExitOnFailure(hr, "failed to add data to Rollback CustomActionData");
373
374 // Nudge the system to get all our rollback data written to disk.
375 WcaCaScriptFlush(hRollbackScript);
376
377 ReleaseNullMem(psfa);
378 }
379
380 hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType,
381 pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine);
382 ExitOnFailure(hr, "Failed to configure service: %ls", pwzServiceName);
383
384 hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE);
385 ExitOnFailure(hr, "failed to send progress message");
386
387 // Per-service cleanup
388 ::CloseServiceHandle(hService);
389 hService = NULL;
390 dwResetPeriodInDays = 0;
391 dwRestartServiceDelayInSeconds = 0;
392 }
393
394LExit:
395 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
396
397 if (lpMsgBuf)
398 {
399 ::LocalFree(lpMsgBuf);
400 }
401
402 if (hService)
403 {
404 ::CloseServiceHandle(hService);
405 }
406
407 if (hSCM)
408 {
409 ::CloseServiceHandle(hSCM);
410 }
411
412 ReleaseMem(psfa);
413
414 ReleaseStr(pwzRebootMessage);
415 ReleaseStr(pwzProgramCommandLine);
416 ReleaseStr(pwzThirdFailureActionType);
417 ReleaseStr(pwzSecondFailureActionType);
418 ReleaseStr(pwzFirstFailureActionType);
419 ReleaseStr(pwzServiceName);
420 ReleaseStr(pwzScriptKey);
421 ReleaseStr(pwzCustomActionData);
422
423 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
424 return WcaFinalize(er);
425}
426
427
428/******************************************************************
429RollbackServiceConfig - entry point for ServiceConfig rollback
430 Custom Action.
431
432NOTE: CustomActionScript Data == wzServiceName\twzFirstFailureActionType\twzSecondFailureActionType\twzThirdFailureActionType\tdwResetPeriodInDays\tdwRestartServiceDelayInSeconds\twzProgramCommandLine\twzRebootMessage\twzServiceName\t...
433*******************************************************************/
434extern "C" UINT __stdcall RollbackServiceConfig(
435 __in MSIHANDLE hInstall
436 )
437{
438 //AssertSz(FALSE, "debug RollbackServiceConfig");
439 HRESULT hr = S_OK;
440 DWORD er = 0;
441
442 LPWSTR pwzCustomActionData = NULL;
443 LPWSTR pwz = NULL;
444
445 LPWSTR pwzScriptKey = NULL;
446 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
447
448 LPWSTR pwzServiceName = NULL;
449 LPWSTR pwzFirstFailureActionType = NULL;
450 LPWSTR pwzSecondFailureActionType = NULL;
451 LPWSTR pwzThirdFailureActionType = NULL;
452 LPWSTR pwzProgramCommandLine = NULL;
453 LPWSTR pwzRebootMessage = NULL;
454 DWORD dwResetPeriodInDays = 0;
455 DWORD dwRestartServiceDelayInSeconds = 0;
456
457 LPVOID lpMsgBuf = NULL;
458 SC_HANDLE hSCM = NULL;
459 SC_HANDLE hService = NULL;
460
461 // initialize
462 hr = WcaInitialize(hInstall, "RollbackServiceConfig");
463 ExitOnFailure(hr, "Failed to initialize 'RollbackServiceConfig'.");
464
465 // Open the Services Control Manager up front.
466 hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
467 if (NULL == hSCM)
468 {
469 er = ::GetLastError();
470 hr = HRESULT_FROM_WIN32(er);
471
472#pragma prefast(push)
473#pragma prefast(disable:25028)
474 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
475#pragma prefast(pop)
476
477 ExitOnFailure(hr, "Failed to get handle to SCM. Error: %ls", (LPWSTR)lpMsgBuf);
478
479 // Make sure we still abort, in case hSCM was NULL but no error was returned from GetLastError
480 ExitOnNull(hSCM, hr, E_POINTER, "Getting handle to SCM reported success, but no handle was returned.");
481 }
482
483 // Get the script key from the CustomAction data and use it to open
484 // the rollback log and read the data over the CustomActionData
485 // because all of the information is in the script data not the
486 // CustomActionData.
487 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
488 ExitOnFailure(hr, "failed to get CustomActionData");
489
490 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
491
492 pwz = pwzCustomActionData;
493
494 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
495 if (!pwzScriptKey)
496 {
497 hr = E_UNEXPECTED;
498 ExitOnFailure(hr, "Failed due to unexpected CustomActionData passed.");
499 }
500 ExitOnFailure(hr, "Failed to read encoding key from CustomActionData.");
501
502 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
503 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
504
505 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzCustomActionData);
506 ExitOnFailure(hr, "Failed to read rollback script into CustomAction data.");
507
508 // Loop through the script's CustomActionData, processing each
509 // service config in turn.
510 pwz = pwzCustomActionData;
511 while (pwz && *pwz)
512 {
513 hr = WcaReadStringFromCaData(&pwz, &pwzServiceName);
514 ExitOnFailure(hr, "failed to process CustomActionData");
515 hr = WcaReadStringFromCaData(&pwz, &pwzFirstFailureActionType);
516 ExitOnFailure(hr, "failed to process CustomActionData");
517 hr = WcaReadStringFromCaData(&pwz, &pwzSecondFailureActionType);
518 ExitOnFailure(hr, "failed to process CustomActionData");
519 hr = WcaReadStringFromCaData(&pwz, &pwzThirdFailureActionType);
520 ExitOnFailure(hr, "failed to process CustomActionData");
521 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwResetPeriodInDays));
522 ExitOnFailure(hr, "failed to process CustomActionData");
523 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int*>(&dwRestartServiceDelayInSeconds));
524 ExitOnFailure(hr, "failed to process CustomActionData");
525 hr = WcaReadStringFromCaData(&pwz, &pwzProgramCommandLine);
526 ExitOnFailure(hr, "failed to process CustomActionData");
527 hr = WcaReadStringFromCaData(&pwz, &pwzRebootMessage);
528 ExitOnFailure(hr, "failed to process CustomActionData");
529
530 WcaLog(LOGMSG_VERBOSE, "Reconfiguring Service: %ls", pwzServiceName);
531
532 // Open the handle with all the permissions we might need.
533 // SERVICE_CHANGE_CONFIG is needed for ChangeServiceConfig2().
534 // SERVICE_START is required in order to handle SC_ACTION_RESTART action.
535 hr = GetService(hSCM, pwzServiceName, SERVICE_CHANGE_CONFIG | SERVICE_START, &hService);
536 ExitOnFailure(hr, "Failed to get service: %ls", pwzServiceName);
537
538 hr = ConfigureService(hSCM, hService, pwzServiceName, dwRestartServiceDelayInSeconds, pwzFirstFailureActionType,
539 pwzSecondFailureActionType, pwzThirdFailureActionType, dwResetPeriodInDays, pwzRebootMessage, pwzProgramCommandLine);
540 ExitOnFailure(hr, "Failed to configure service: %ls", pwzServiceName);
541
542 hr = WcaProgressMessage(COST_SERVICECONFIG, FALSE);
543 ExitOnFailure(hr, "failed to send progress message");
544
545 // Per-service cleanup
546 ::CloseServiceHandle(hService);
547 hService = NULL;
548 dwResetPeriodInDays = 0;
549 dwRestartServiceDelayInSeconds = 0;
550 }
551
552LExit:
553 if (lpMsgBuf) // Allocated with FormatString.
554 {
555 ::LocalFree(lpMsgBuf);
556 }
557
558 if (hService)
559 {
560 ::CloseServiceHandle(hService);
561 }
562
563 if (hSCM)
564 {
565 ::CloseServiceHandle(hSCM);
566 }
567
568 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
569
570 ReleaseStr(pwzRebootMessage);
571 ReleaseStr(pwzProgramCommandLine);
572 ReleaseStr(pwzThirdFailureActionType);
573 ReleaseStr(pwzSecondFailureActionType);
574 ReleaseStr(pwzFirstFailureActionType);
575 ReleaseStr(pwzServiceName);
576 ReleaseStr(pwzScriptKey);
577 ReleaseStr(pwzCustomActionData);
578
579 er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
580 return WcaFinalize(er);
581}
582
583
584/**********************************************************
585GetSCActionType - helper function to return the SC_ACTION_TYPE
586for a given string matching the allowed set.
587REBOOT, RESTART, RUN_COMMAND and NONE
588**********************************************************/
589static SC_ACTION_TYPE GetSCActionType(
590 __in LPCWSTR pwzActionTypeName
591 )
592{
593 SC_ACTION_TYPE actionType;
594
595 // verify that action types are valid. if not, just default to NONE
596 if (0 == lstrcmpiW(c_wzActionTypeReboot, pwzActionTypeName))
597 {
598 actionType = SC_ACTION_REBOOT;
599 }
600 else if (0 == lstrcmpiW(c_wzActionTypeRestart, pwzActionTypeName))
601 {
602 actionType = SC_ACTION_RESTART;
603 }
604 else if (0 == lstrcmpiW(c_wzActionTypeRunCommand, pwzActionTypeName))
605 {
606 actionType = SC_ACTION_RUN_COMMAND;
607 }
608 else
609 {
610 // default to none
611 actionType = SC_ACTION_NONE;
612 }
613
614 return actionType;
615}
616
617
618static HRESULT GetSCActionTypeString(
619 __in SC_ACTION_TYPE type,
620 __out_ecount(cchActionTypeString) LPWSTR wzActionTypeString,
621 __in DWORD cchActionTypeString
622 )
623{
624 HRESULT hr = S_OK;
625
626 switch (type)
627 {
628 case SC_ACTION_REBOOT:
629 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeReboot);
630 ExitOnFailure(hr, "Failed to copy 'reboot' into action type.");
631 break;
632 case SC_ACTION_RESTART:
633 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeRestart);
634 ExitOnFailure(hr, "Failed to copy 'restart' into action type.");
635 break;
636 case SC_ACTION_RUN_COMMAND:
637 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeRunCommand);
638 ExitOnFailure(hr, "Failed to copy 'runCommand' into action type.");
639 break;
640 case SC_ACTION_NONE:
641 hr = StringCchCopyW(wzActionTypeString, cchActionTypeString, c_wzActionTypeNone);
642 ExitOnFailure(hr, "Failed to copy 'none' into action type.");
643 break;
644 default:
645 break;
646 }
647
648LExit:
649 return hr;
650}
651
652
653static HRESULT GetService(
654 __in SC_HANDLE hSCM,
655 __in LPCWSTR wzService,
656 __in DWORD dwOpenServiceAccess,
657 __out SC_HANDLE* phService
658 )
659{
660 HRESULT hr = S_OK;
661 DWORD er = ERROR_SUCCESS;
662 LPVOID lpMsgBuf = NULL;
663
664 *phService = ::OpenServiceW(hSCM, wzService, dwOpenServiceAccess);
665 if (NULL == *phService)
666 {
667 er = ::GetLastError();
668 hr = HRESULT_FROM_WIN32(er);
669 if (ERROR_SERVICE_DOES_NOT_EXIST == er)
670 {
671 ExitOnFailure(hr, "Service '%ls' does not exist on this system.", wzService);
672 }
673 else
674 {
675#pragma prefast(push)
676#pragma prefast(disable:25028)
677 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
678#pragma prefast(pop)
679
680 ExitOnFailure(hr, "Failed to get handle to the service '%ls'. Error: %ls", wzService, (LPWSTR)lpMsgBuf);
681 }
682 }
683
684LExit:
685 if (lpMsgBuf) // Allocated with FormatString.
686 {
687 ::LocalFree(lpMsgBuf);
688 }
689
690 return hr;
691}
692
693
694static HRESULT ConfigureService(
695 __in SC_HANDLE /*hSCM*/,
696 __in SC_HANDLE hService,
697 __in LPCWSTR wzServiceName,
698 __in DWORD dwRestartServiceDelayInSeconds,
699 __in LPCWSTR wzFirstFailureActionType,
700 __in LPCWSTR wzSecondFailureActionType,
701 __in LPCWSTR wzThirdFailureActionType,
702 __in DWORD dwResetPeriodInDays,
703 __in LPWSTR wzRebootMessage,
704 __in LPWSTR wzProgramCommandLine
705 )
706{
707 HRESULT hr = S_OK;
708 DWORD er = ERROR_SUCCESS;
709
710 HANDLE hToken = NULL;
711 TOKEN_PRIVILEGES priv = { 0 };
712 TOKEN_PRIVILEGES* pPrevPriv = NULL;
713 DWORD cbPrevPriv = 0;
714 BOOL fAdjustedPrivileges = FALSE;
715
716 SC_ACTION actions[3]; // the UI always shows 3 actions, so we'll always do 3
717 SERVICE_FAILURE_ACTIONSW sfa;
718 LPVOID lpMsgBuf = NULL;
719
720 // Always get the shutdown privilege in case we need to configure service to reboot.
721 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
722 {
723 ExitWithLastError(hr, "Failed to get process token.");
724 }
725
726 priv.PrivilegeCount = 1;
727 priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
728 if (!::LookupPrivilegeValueW(NULL, L"SeShutdownPrivilege", &priv.Privileges[0].Luid))
729 {
730 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
731 }
732
733 cbPrevPriv = sizeof(TOKEN_PRIVILEGES);
734 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(MemAlloc(cbPrevPriv, TRUE));
735 ExitOnNull(pPrevPriv, hr, E_OUTOFMEMORY, "Failed to allocate memory for empty previous privileges.");
736
737 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
738 {
739 LPVOID pv = MemReAlloc(pPrevPriv, cbPrevPriv, TRUE);
740 ExitOnNull(pv, hr, E_OUTOFMEMORY, "Failed to allocate memory for previous privileges.");
741 pPrevPriv = static_cast<TOKEN_PRIVILEGES*>(pv);
742
743 if (!::AdjustTokenPrivileges(hToken, FALSE, &priv, cbPrevPriv, pPrevPriv, &cbPrevPriv))
744 {
745 ExitWithLastError(hr, "Failed to get shutdown privilege LUID.");
746 }
747 }
748
749 fAdjustedPrivileges = TRUE;
750
751 // build up SC_ACTION array
752 // TODO: why is delay only respected when SC_ACTION_RESTART is requested?
753 actions[0].Type = GetSCActionType(wzFirstFailureActionType);
754 actions[0].Delay = 0;
755 if (SC_ACTION_RESTART == actions[0].Type)
756 {
757 actions[0].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
758 }
759
760 actions[1].Type = GetSCActionType(wzSecondFailureActionType);
761 actions[1].Delay = 0;
762 if (SC_ACTION_RESTART == actions[1].Type)
763 {
764 actions[1].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
765 }
766
767 actions[2].Type = GetSCActionType(wzThirdFailureActionType);
768 actions[2].Delay = 0;
769 if (SC_ACTION_RESTART == actions[2].Type)
770 {
771 actions[2].Delay = dwRestartServiceDelayInSeconds * 1000; // seconds to milliseconds
772 }
773
774 // build up the SERVICE_FAILURE_ACTIONSW struct
775 sfa.dwResetPeriod = dwResetPeriodInDays * (24 * 60 * 60); // days to seconds
776 sfa.lpRebootMsg = wzRebootMessage;
777 sfa.lpCommand = wzProgramCommandLine;
778 sfa.cActions = countof(actions);
779 sfa.lpsaActions = actions;
780
781 // Call ChangeServiceConfig2 to actually set up the failure actions
782 if (!::ChangeServiceConfig2W(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPVOID)&sfa))
783 {
784 er = ::GetLastError();
785 hr = HRESULT_FROM_WIN32(er);
786
787#pragma prefast(push)
788#pragma prefast(disable:25028)
789 ::FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, er, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL);
790#pragma prefast(pop)
791
792 // Check if this is a service that can't be modified.
793 if (ERROR_CANNOT_DETECT_PROCESS_ABORT == er)
794 {
795 WcaLog(LOGMSG_STANDARD, "WARNING: Service \"%ls\" is not configurable on this server and will not be set.", wzServiceName);
796 }
797 ExitOnFailure(hr, "Cannot change service configuration. Error: %ls", (LPWSTR)lpMsgBuf);
798
799 if (lpMsgBuf)
800 {
801 ::LocalFree(lpMsgBuf);
802 lpMsgBuf = NULL;
803 }
804 }
805
806LExit:
807 if (lpMsgBuf)
808 {
809 ::LocalFree(lpMsgBuf);
810 }
811
812 if (fAdjustedPrivileges)
813 {
814 ::AdjustTokenPrivileges(hToken, FALSE, pPrevPriv, 0, NULL, NULL);
815 }
816
817 ReleaseMem(pPrevPriv);
818 ReleaseHandle(hToken);
819
820 return hr;
821}
diff --git a/src/ext/Util/ca/shellexecca.cpp b/src/ext/Util/ca/shellexecca.cpp
new file mode 100644
index 00000000..ea21d3bd
--- /dev/null
+++ b/src/ext/Util/ca/shellexecca.cpp
@@ -0,0 +1,271 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5HRESULT ShellExec(
6 __in LPCWSTR wzTarget,
7 __in BOOL fUnelevated
8 )
9{
10 HRESULT hr = S_OK;
11 LPWSTR sczWorkingDirectory = NULL;
12
13 // a reasonable working directory (not the system32 default from MSI) is the directory where the target lives
14 hr = PathGetDirectory(wzTarget, &sczWorkingDirectory);
15 ExitOnFailure(hr, "failed to get directory for target: %ls", wzTarget);
16
17 if (!DirExists(sczWorkingDirectory, NULL))
18 {
19 ReleaseNullStr(sczWorkingDirectory);
20 }
21
22 if (fUnelevated)
23 {
24 hr = ShelExecUnelevated(wzTarget, NULL, NULL, sczWorkingDirectory, SW_SHOWDEFAULT);
25 ExitOnFailure(hr, "ShelExecUnelevated failed with target %ls", wzTarget);
26 }
27 else
28 {
29 HINSTANCE hinst = ::ShellExecuteW(NULL, NULL, wzTarget, NULL, sczWorkingDirectory, SW_SHOWDEFAULT);
30 if (hinst <= HINSTANCE(32))
31 {
32 LONG64 code = reinterpret_cast<LONG64>(hinst);
33 switch (code)
34 {
35 case ERROR_FILE_NOT_FOUND:
36 hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
37 break;
38 case ERROR_PATH_NOT_FOUND:
39 hr = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
40 break;
41 case ERROR_BAD_FORMAT:
42 hr = HRESULT_FROM_WIN32(ERROR_BAD_FORMAT);
43 break;
44 case SE_ERR_ASSOCINCOMPLETE:
45 case SE_ERR_NOASSOC:
46 hr = HRESULT_FROM_WIN32(ERROR_NO_ASSOCIATION);
47 break;
48 case SE_ERR_DDEBUSY:
49 case SE_ERR_DDEFAIL:
50 case SE_ERR_DDETIMEOUT:
51 hr = HRESULT_FROM_WIN32(ERROR_DDE_FAIL);
52 break;
53 case SE_ERR_DLLNOTFOUND:
54 hr = HRESULT_FROM_WIN32(ERROR_DLL_NOT_FOUND);
55 break;
56 case SE_ERR_OOM:
57 hr = E_OUTOFMEMORY;
58 break;
59 case SE_ERR_ACCESSDENIED:
60 hr = E_ACCESSDENIED;
61 break;
62 default:
63 hr = E_FAIL;
64 }
65
66 ExitOnFailure(hr, "ShellExec failed with return code %llu.", code);
67 }
68 }
69
70
71LExit:
72 ReleaseStr(sczWorkingDirectory);
73 return hr;
74}
75
76extern "C" UINT __stdcall WixShellExec(
77 __in MSIHANDLE hInstall
78 )
79{
80 Assert(hInstall);
81 HRESULT hr = S_OK;
82 UINT er = ERROR_SUCCESS;
83 LPWSTR pwzTarget = NULL;
84
85 hr = WcaInitialize(hInstall, "WixShellExec");
86 ExitOnFailure(hr, "failed to initialize");
87
88 hr = WcaGetFormattedProperty(L"WixShellExecTarget", &pwzTarget);
89 ExitOnFailure(hr, "failed to get WixShellExecTarget");
90
91 WcaLog(LOGMSG_VERBOSE, "WixShellExecTarget is %ls", pwzTarget);
92
93 if (!pwzTarget || !*pwzTarget)
94 {
95 hr = E_INVALIDARG;
96 ExitOnFailure(hr, "failed to get WixShellExecTarget");
97 }
98
99 hr = ShellExec(pwzTarget, FALSE);
100 ExitOnFailure(hr, "failed to launch target");
101
102LExit:
103 ReleaseStr(pwzTarget);
104
105 if (FAILED(hr))
106 {
107 er = ERROR_INSTALL_FAILURE;
108 }
109 return WcaFinalize(er);
110}
111
112extern "C" UINT __stdcall WixUnelevatedShellExec(
113 __in MSIHANDLE hInstall
114 )
115{
116 Assert(hInstall);
117 HRESULT hr = S_OK;
118 UINT er = ERROR_SUCCESS;
119 LPWSTR pwzTarget = NULL;
120
121 hr = WcaInitialize(hInstall, "WixUnelevatedShellExec");
122 ExitOnFailure(hr, "failed to initialize");
123
124 hr = WcaGetFormattedProperty(L"WixUnelevatedShellExecTarget", &pwzTarget);
125 ExitOnFailure(hr, "failed to get WixUnelevatedShellExecTarget");
126
127 WcaLog(LOGMSG_VERBOSE, "WixUnelevatedShellExecTarget is %ls", pwzTarget);
128
129 if (!pwzTarget || !*pwzTarget)
130 {
131 hr = E_INVALIDARG;
132 ExitOnFailure(hr, "failed to get WixShellExecTarget");
133 }
134
135 hr = ShellExec(pwzTarget, TRUE);
136 ExitOnFailure(hr, "failed to launch target");
137
138LExit:
139 ReleaseStr(pwzTarget);
140
141 if (FAILED(hr))
142 {
143 er = ERROR_INSTALL_FAILURE;
144 }
145 return WcaFinalize(er);
146}
147
148//
149// ExtractBinary extracts the data from the Binary table row with the given ID into a file.
150//
151HRESULT ExtractBinary(
152 __in LPCWSTR wzBinaryId,
153 __out BYTE** pbData,
154 __out DWORD* pcbData
155 )
156{
157 HRESULT hr = S_OK;
158 LPWSTR pwzSql = NULL;
159 PMSIHANDLE hView;
160 PMSIHANDLE hRec;
161
162 // make sure we're not horked from the get-go
163 hr = WcaTableExists(L"Binary");
164 if (S_OK != hr)
165 {
166 if (SUCCEEDED(hr))
167 {
168 hr = E_UNEXPECTED;
169 }
170 ExitOnFailure(hr, "There is no Binary table.");
171 }
172
173 ExitOnNull(wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be null");
174 ExitOnNull(*wzBinaryId, hr, E_INVALIDARG, "Binary ID cannot be empty string");
175
176 hr = StrAllocFormatted(&pwzSql, L"SELECT `Data` FROM `Binary` WHERE `Name`=\'%s\'", wzBinaryId);
177 ExitOnFailure(hr, "Failed to allocate Binary table query.");
178
179 hr = WcaOpenExecuteView(pwzSql, &hView);
180 ExitOnFailure(hr, "Failed to open view on Binary table");
181
182 hr = WcaFetchSingleRecord(hView, &hRec);
183 ExitOnFailure(hr, "Failed to retrieve request from Binary table");
184
185 hr = WcaGetRecordStream(hRec, 1, pbData, pcbData);
186 ExitOnFailure(hr, "Failed to read Binary.Data.");
187
188LExit:
189 ReleaseStr(pwzSql);
190
191 return hr;
192}
193
194extern "C" UINT __stdcall WixShellExecBinary(
195 __in MSIHANDLE hInstall
196 )
197{
198 Assert(hInstall);
199 HRESULT hr = S_OK;
200 UINT er = ERROR_SUCCESS;
201 LPWSTR pwzBinary = NULL;
202 LPWSTR pwzFilename = NULL;
203 BYTE* pbData = NULL;
204 DWORD cbData = 0;
205 HANDLE hFile = INVALID_HANDLE_VALUE;
206
207#if 0
208 ::MessageBoxA(0, "WixShellExecBinary", "-->> ATTACH HERE", MB_OK);
209#endif
210
211 hr = WcaInitialize(hInstall, "WixShellExecBinary");
212 ExitOnFailure(hr, "failed to initialize");
213
214 hr = WcaGetFormattedProperty(L"WixShellExecBinaryId", &pwzBinary);
215 ExitOnFailure(hr, "failed to get WixShellExecBinaryId");
216
217 WcaLog(LOGMSG_VERBOSE, "WixShellExecBinaryId is %ls", pwzBinary);
218
219 if (!pwzBinary || !*pwzBinary)
220 {
221 hr = E_INVALIDARG;
222 ExitOnFailure(hr, "failed to get WixShellExecBinaryId");
223 }
224
225 // get temporary path for extracted file
226 StrAlloc(&pwzFilename, MAX_PATH);
227 ExitOnFailure(hr, "Failed to allocate temporary path");
228 ::GetTempPathW(MAX_PATH, pwzFilename);
229 hr = ::StringCchCatW(pwzFilename, MAX_PATH, pwzBinary);
230 ExitOnFailure(hr, "Failed to append filename.");
231
232 // grab the bits
233 hr = ExtractBinary(pwzBinary, &pbData, &cbData);
234 ExitOnFailure(hr, "failed to extract binary data");
235
236 // write 'em to the temp file
237 hFile = ::CreateFileW(pwzFilename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
238 if (INVALID_HANDLE_VALUE == hFile)
239 {
240 ExitWithLastError(hr, "Failed to open new temp file: %ls", pwzFilename);
241 }
242
243 DWORD cbWritten = 0;
244 if (!::WriteFile(hFile, pbData, cbData, &cbWritten, NULL))
245 {
246 ExitWithLastError(hr, "Failed to write data to new temp file: %ls", pwzFilename);
247 }
248
249 // close it
250 ::CloseHandle(hFile);
251 hFile = INVALID_HANDLE_VALUE;
252
253 // and run it
254 hr = ShellExec(pwzFilename, FALSE);
255 ExitOnFailure(hr, "failed to launch target: %ls", pwzFilename);
256
257LExit:
258 ReleaseStr(pwzBinary);
259 ReleaseStr(pwzFilename);
260 ReleaseMem(pbData);
261 if (INVALID_HANDLE_VALUE != hFile)
262 {
263 ::CloseHandle(hFile);
264 }
265
266 if (FAILED(hr))
267 {
268 er = ERROR_INSTALL_FAILURE;
269 }
270 return WcaFinalize(er);
271}
diff --git a/src/ext/Util/ca/test.cpp b/src/ext/Util/ca/test.cpp
new file mode 100644
index 00000000..c4d215f0
--- /dev/null
+++ b/src/ext/Util/ca/test.cpp
@@ -0,0 +1,269 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5#define WIXCA_UITHREAD_CLASS_WINDOW L"WixCaMessageWindow"
6
7extern HMODULE g_hInstCADLL;
8
9
10// structs
11
12struct UITHREAD_CONTEXT
13{
14 HANDLE hInitializedEvent;
15 HINSTANCE hInstance;
16 HWND hWnd;
17};
18
19
20// internal function declarations
21
22static HRESULT CreateMessageWindow(
23 __out HWND* phWnd
24 );
25
26static void CloseMessageWindow(
27 __in HWND hWnd
28 );
29
30static DWORD WINAPI ThreadProc(
31 __in LPVOID pvContext
32 );
33
34static LRESULT CALLBACK WndProc(
35 __in HWND hWnd,
36 __in UINT uMsg,
37 __in WPARAM wParam,
38 __in LPARAM lParam
39 );
40
41
42/******************************************************************
43WixFailWhenDeferred - entry point for WixFailWhenDeferred
44 custom action which always fails when running as a deferred
45 custom action (otherwise it blindly succeeds). It's useful when
46 testing the rollback of deferred custom actions: Schedule it
47 immediately after the rollback/deferred CA pair you're testing
48 and it will fail, causing your rollback CA to get invoked.
49********************************************************************/
50extern "C" UINT __stdcall WixFailWhenDeferred(
51 __in MSIHANDLE hInstall
52 )
53{
54 return ::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS;
55}
56
57/******************************************************************
58WixWaitForEvent - entry point for WixWaitForEvent custom action
59 which waits for either the WixWaitForEventFail or
60 WixWaitForEventSucceed named auto reset events. Signaling the
61 WixWaitForEventFail event will return ERROR_INSTALL_FAILURE or
62 signaling the WixWaitForEventSucceed event will return
63 ERROR_SUCCESS. Both events are declared in the Global\ namespace.
64********************************************************************/
65extern "C" UINT __stdcall WixWaitForEvent(
66 __in MSIHANDLE hInstall
67 )
68{
69 HRESULT hr = S_OK;
70 UINT er = ERROR_SUCCESS;
71 HWND hMessageWindow = NULL;
72 LPCWSTR wzSDDL = L"D:(A;;GA;;;WD)";
73 OS_VERSION version = OS_VERSION_UNKNOWN;
74 DWORD dwServicePack = 0;
75 PSECURITY_DESCRIPTOR pSD = NULL;
76 SECURITY_ATTRIBUTES sa = { };
77 HANDLE rghEvents[2];
78
79 hr = WcaInitialize(hInstall, "WixWaitForEvent");
80 ExitOnFailure(hr, "Failed to initialize.");
81
82 // Create a window to prevent shutdown requests.
83 hr = CreateMessageWindow(&hMessageWindow);
84 ExitOnFailure(hr, "Failed to create message window.");
85
86 // If running on Vista/2008 or newer use integrity enhancements.
87 OsGetVersion(&version, &dwServicePack);
88 if (OS_VERSION_VISTA <= version)
89 {
90 // Add SACL to allow Everyone to signal from a medium integrity level.
91 wzSDDL = L"D:(A;;GA;;;WD)S:(ML;;NW;;;ME)";
92 }
93
94 // Create the security descriptor and attributes for the events.
95 if (!::ConvertStringSecurityDescriptorToSecurityDescriptorW(wzSDDL, SDDL_REVISION_1, &pSD, NULL))
96 {
97 ExitWithLastError(hr, "Failed to create the security descriptor for the events.");
98 }
99
100 sa.nLength = sizeof(sa);
101 sa.lpSecurityDescriptor = pSD;
102 sa.bInheritHandle = FALSE;
103
104 rghEvents[0] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventFail");
105 ExitOnNullWithLastError(rghEvents[0], hr, "Failed to create the Global\\WixWaitForEventFail event.");
106
107 rghEvents[1] = ::CreateEventW(&sa, FALSE, FALSE, L"Global\\WixWaitForEventSucceed");
108 ExitOnNullWithLastError(rghEvents[1], hr, "Failed to create the Global\\WixWaitForEventSucceed event.");
109
110 // Wait for either of the events to be signaled and handle accordingly.
111 er = ::WaitForMultipleObjects(countof(rghEvents), rghEvents, FALSE, INFINITE);
112 switch (er)
113 {
114 case WAIT_OBJECT_0 + 0:
115 er = ERROR_INSTALL_FAILURE;
116 break;
117 case WAIT_OBJECT_0 + 1:
118 er = ERROR_SUCCESS;
119 break;
120 default:
121 ExitOnWin32Error(er, hr, "Unexpected failure.");
122 }
123
124LExit:
125 ReleaseHandle(rghEvents[1]);
126 ReleaseHandle(rghEvents[0]);
127
128 if (pSD)
129 {
130 ::LocalFree(pSD);
131 }
132
133 if (hMessageWindow)
134 {
135 CloseMessageWindow(hMessageWindow);
136 }
137
138 if (FAILED(hr))
139 {
140 er = ERROR_INSTALL_FAILURE;
141 }
142
143 return WcaFinalize(er);
144}
145
146
147// internal function definitions
148
149static HRESULT CreateMessageWindow(
150 __out HWND* phWnd
151 )
152{
153 HRESULT hr = S_OK;
154 HANDLE rgWaitHandles[2] = { };
155 UITHREAD_CONTEXT context = { };
156
157 // Create event to signal after the UI thread / window is initialized.
158 rgWaitHandles[0] = ::CreateEventW(NULL, TRUE, FALSE, NULL);
159 ExitOnNullWithLastError(rgWaitHandles[0], hr, "Failed to create initialization event.");
160
161 // Pass necessary information to create the window.
162 context.hInitializedEvent = rgWaitHandles[0];
163 context.hInstance = (HINSTANCE)g_hInstCADLL;
164
165 // Create our separate UI thread.
166 rgWaitHandles[1] = ::CreateThread(NULL, 0, ThreadProc, &context, 0, NULL);
167 ExitOnNullWithLastError(rgWaitHandles[1], hr, "Failed to create the UI thread.");
168
169 // Wait for either the thread to be initialized or the window to exit / fail prematurely.
170 ::WaitForMultipleObjects(countof(rgWaitHandles), rgWaitHandles, FALSE, INFINITE);
171
172 // Pass the window back to the caller.
173 *phWnd = context.hWnd;
174
175LExit:
176 ReleaseHandle(rgWaitHandles[1]);
177 ReleaseHandle(rgWaitHandles[0]);
178
179 return hr;
180}
181
182static void CloseMessageWindow(
183 __in HWND hWnd
184 )
185{
186 if (::IsWindow(hWnd))
187 {
188 ::PostMessageW(hWnd, WM_CLOSE, 0, 0);
189 }
190}
191
192static DWORD WINAPI ThreadProc(
193 __in LPVOID pvContext
194 )
195{
196 HRESULT hr = S_OK;
197 UITHREAD_CONTEXT* pContext = static_cast<UITHREAD_CONTEXT*>(pvContext);
198
199 WNDCLASSW wc = { };
200 BOOL fRegistered = TRUE;
201 HWND hWnd = NULL;
202
203 BOOL fRet = FALSE;
204 MSG msg = { };
205
206 wc.lpfnWndProc = WndProc;
207 wc.hInstance = pContext->hInstance;
208 wc.lpszClassName = WIXCA_UITHREAD_CLASS_WINDOW;
209
210 if (!::RegisterClassW(&wc))
211 {
212 ExitWithLastError(hr, "Failed to register window.");
213 }
214
215 fRegistered = TRUE;
216
217 // Create the window to handle reboots without activating it.
218 hWnd = ::CreateWindowExW(WS_EX_TOOLWINDOW, wc.lpszClassName, NULL, WS_POPUP | WS_VISIBLE, CW_USEDEFAULT, SW_SHOWNA, 0, 0, HWND_DESKTOP, NULL, pContext->hInstance, NULL);
219 ExitOnNullWithLastError(hWnd, hr, "Failed to create window.");
220
221 // Persist the window handle and let the caller know we've initialized.
222 pContext->hWnd = hWnd;
223 ::SetEvent(pContext->hInitializedEvent);
224
225 // Pump messages until the window is closed.
226 while (0 != (fRet = ::GetMessageW(&msg, NULL, 0, 0)))
227 {
228 if (-1 == fRet)
229 {
230 hr = E_UNEXPECTED;
231 ExitOnFailure(hr, "Unexpected return value from message pump.");
232 }
233 else if (!::IsDialogMessageW(msg.hwnd, &msg))
234 {
235 ::TranslateMessage(&msg);
236 ::DispatchMessageW(&msg);
237 }
238 }
239
240LExit:
241 if (fRegistered)
242 {
243 ::UnregisterClassW(WIXCA_UITHREAD_CLASS_WINDOW, pContext->hInstance);
244 }
245
246 return hr;
247}
248
249static LRESULT CALLBACK WndProc(
250 __in HWND hWnd,
251 __in UINT uMsg,
252 __in WPARAM wParam,
253 __in LPARAM lParam
254 )
255{
256 switch (uMsg)
257 {
258 case WM_QUERYENDSESSION:
259 // Prevent the process from being shut down.
260 WcaLog(LOGMSG_VERBOSE, "Disallowed system request to shut down the custom action server.");
261 return FALSE;
262
263 case WM_DESTROY:
264 ::PostQuitMessage(0);
265 return 0;
266 }
267
268 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
269}
diff --git a/src/ext/Util/ca/utilca.cpp b/src/ext/Util/ca/utilca.cpp
new file mode 100644
index 00000000..37664a1c
--- /dev/null
+++ b/src/ext/Util/ca/utilca.cpp
@@ -0,0 +1,3 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
diff --git a/src/ext/Util/ca/utilca.def b/src/ext/Util/ca/utilca.def
new file mode 100644
index 00000000..412d86a3
--- /dev/null
+++ b/src/ext/Util/ca/utilca.def
@@ -0,0 +1,91 @@
1; Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3
4LIBRARY "utilca"
5
6EXPORTS
7; BroadcastSettingChange.cpp
8 WixBroadcastSettingChange
9 WixBroadcastEnvironmentChange
10; checkreboot.cpp
11 WixCheckRebootRequired
12; closeapps.cpp
13 WixCloseApplications
14 WixCloseApplicationsDeferred
15; exitearlywithsuccess.cpp
16 WixExitEarlyWithSuccess
17; FormatFiles.cpp
18 WixSchedFormatFiles
19 WixExecFormatFiles
20; osinfo.cpp
21 WixQueryOsInfo
22 WixQueryOsDirs
23 WixQueryOsWellKnownSID
24 WixQueryOsDriverInfo
25; netshortcuts.cpp
26 WixSchedInternetShortcuts
27 WixCreateInternetShortcuts
28 WixRollbackInternetShortcuts
29; qtexecca.cpp
30 CAQuietExec
31 CAQuietExec64
32 WixQuietExec
33 WixQuietExec64
34 WixSilentExec
35 WixSilentExec64
36; RemoveFoldersEx.cpp
37 WixRemoveFoldersEx
38; RemoveRegistryKeysEx.cpp
39 WixRemoveRegistryKeysEx
40;scaexec.cpp
41 RegisterPerfCounterData
42 UnregisterPerfCounterData
43 RegisterPerfmon
44 UnregisterPerfmon
45 CreateSmb
46 DropSmb
47 CreateUser
48 CreateUserRollback
49 RemoveUser
50;scasched.cpp
51 ConfigurePerfmonInstall
52 ConfigurePerfmonUninstall
53 ConfigureSmbInstall
54 ConfigureSmbUninstall
55 ConfigureUsers
56 InstallPerfCounterData
57 UninstallPerfCounterData
58 ConfigurePerfmonManifestRegister
59 ConfigurePerfmonManifestUnregister
60 ConfigureEventManifestRegister
61 ConfigureEventManifestUnregister
62; RestartManager.cpp
63 WixRegisterRestartResources
64; secureobj.cpp
65 SchedSecureObjects
66 SchedSecureObjectsRollback
67 ExecSecureObjects
68 ExecSecureObjectsRollback
69; serviceconfig.cpp
70 SchedServiceConfig
71 ExecServiceConfig
72 RollbackServiceConfig
73; shellexecca.cpp
74 WixShellExec
75 WixShellExecBinary
76 WixUnelevatedShellExec
77; test.cpp
78 WixFailWhenDeferred
79 WixWaitForEvent
80; TouchFile.cpp
81 WixTouchFileDuringInstall
82 WixTouchFileDuringUninstall
83 WixExecuteTouchFile
84; xmlfile.cpp
85 SchedXmlFile
86 ExecXmlFile
87 ExecXmlFileRollback
88; xmlconfig.cpp
89 SchedXmlConfig
90 ExecXmlConfig
91 ExecXmlConfigRollback
diff --git a/src/ext/Util/ca/utilca.vcxproj b/src/ext/Util/ca/utilca.vcxproj
new file mode 100644
index 00000000..7b64db95
--- /dev/null
+++ b/src/ext/Util/ca/utilca.vcxproj
@@ -0,0 +1,106 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project DefaultTargets="Build" ToolsVersion="16.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
5 <ItemGroup Label="ProjectConfigurations">
6 <ProjectConfiguration Include="Debug|ARM64">
7 <Configuration>Debug</Configuration>
8 <Platform>ARM64</Platform>
9 </ProjectConfiguration>
10 <ProjectConfiguration Include="Release|ARM64">
11 <Configuration>Release</Configuration>
12 <Platform>ARM64</Platform>
13 </ProjectConfiguration>
14 <ProjectConfiguration Include="Debug|X64">
15 <Configuration>Debug</Configuration>
16 <Platform>X64</Platform>
17 </ProjectConfiguration>
18 <ProjectConfiguration Include="Release|X64">
19 <Configuration>Release</Configuration>
20 <Platform>X64</Platform>
21 </ProjectConfiguration>
22 <ProjectConfiguration Include="Debug|Win32">
23 <Configuration>Debug</Configuration>
24 <Platform>Win32</Platform>
25 </ProjectConfiguration>
26 <ProjectConfiguration Include="Release|Win32">
27 <Configuration>Release</Configuration>
28 <Platform>Win32</Platform>
29 </ProjectConfiguration>
30 </ItemGroup>
31
32 <PropertyGroup Label="Globals">
33 <ProjectGuid>{076018F7-19BD-423A-ABBF-229273DA08D8}</ProjectGuid>
34 <ConfigurationType>DynamicLibrary</ConfigurationType>
35 <TargetName>utilca</TargetName>
36 <PlatformToolset>v142</PlatformToolset>
37 <CharacterSet>Unicode</CharacterSet>
38 <ProjectModuleDefinitionFile>utilca.def</ProjectModuleDefinitionFile>
39 <Description>WiX Toolset Util CustomAction</Description>
40 </PropertyGroup>
41
42 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
43 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
44
45 <PropertyGroup>
46 <ProjectAdditionalLinkLibraries>activeds.lib;adsiid.lib;msi.lib;netapi32.lib;shlwapi.lib</ProjectAdditionalLinkLibraries>
47 </PropertyGroup>
48
49 <ItemGroup>
50 <ClCompile Include="BroadcastSettingChange.cpp" />
51 <ClCompile Include="CheckReboot.cpp" />
52 <ClCompile Include="CloseApps.cpp" />
53 <ClCompile Include="dllmain.cpp">
54 <PrecompiledHeader>Create</PrecompiledHeader>
55 </ClCompile>
56 <ClCompile Include="exitearlywithsuccess.cpp" />
57 <ClCompile Include="FormatFiles.cpp" />
58 <ClCompile Include="netshortcuts.cpp" />
59 <ClCompile Include="OsInfo.cpp" />
60 <ClCompile Include="qtexecca.cpp" />
61 <ClCompile Include="RemoveFoldersEx.cpp" />
62 <ClCompile Include="RemoveRegistryKeysEx.cpp" />
63 <ClCompile Include="RestartManager.cpp" />
64 <ClCompile Include="scaexec.cpp" />
65 <ClCompile Include="scamanifest.cpp" />
66 <ClCompile Include="scaperf.cpp" />
67 <ClCompile Include="scaperfexec.cpp" />
68 <ClCompile Include="scasched.cpp" />
69 <ClCompile Include="scasmbexec.cpp" />
70 <ClCompile Include="scasmbsched.cpp" />
71 <ClCompile Include="scauser.cpp" />
72 <ClCompile Include="secureobj.cpp" />
73 <ClCompile Include="serviceconfig.cpp" />
74 <ClCompile Include="shellexecca.cpp" />
75 <ClCompile Include="test.cpp" />
76 <ClCompile Include="TouchFile.cpp" />
77 <ClCompile Include="utilca.cpp" />
78 <ClCompile Include="XmlConfig.cpp" />
79 <ClCompile Include="XmlFile.cpp" />
80 </ItemGroup>
81
82 <ItemGroup>
83 <ClInclude Include="caDecor.h" />
84 <ClInclude Include="cost.h" />
85 <ClInclude Include="CustomMsiErrors.h" />
86 <ClInclude Include="precomp.h" />
87 <ClInclude Include="sca.h" />
88 <ClInclude Include="scacost.h" />
89 <ClInclude Include="scasmb.h" />
90 <ClInclude Include="scasmbexec.h" />
91 <ClInclude Include="scauser.h" />
92 </ItemGroup>
93
94 <ItemGroup>
95 <None Include="utilca.def" />
96 </ItemGroup>
97
98 <ItemGroup>
99 <PackageReference Include="WixToolset.Dutil" Version="4.0.72" />
100 <PackageReference Include="WixToolset.WcaUtil" Version="4.0.19" />
101 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />
102 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" />
103 </ItemGroup>
104
105 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
106</Project>