aboutsummaryrefslogtreecommitdiff
path: root/src/ca/XmlConfig.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-15 21:46:30 -0600
committerGitHub <noreply@github.com>2018-12-15 21:46:30 -0600
commitf7020c0d16baf2b960e7123e233e20c519f6a340 (patch)
treed2cd464ee15b2b3f304ff780c531b39bb292d331 /src/ca/XmlConfig.cpp
parent6ed8d107e6edf16956c778bda3573f8d7a7690fc (diff)
downloadwix-f7020c0d16baf2b960e7123e233e20c519f6a340.tar.gz
wix-f7020c0d16baf2b960e7123e233e20c519f6a340.tar.bz2
wix-f7020c0d16baf2b960e7123e233e20c519f6a340.zip
Import implementation of UtilCA from old repo's WixCA/scasched/scaexec. (#3)
Diffstat (limited to 'src/ca/XmlConfig.cpp')
-rw-r--r--src/ca/XmlConfig.cpp1099
1 files changed, 1099 insertions, 0 deletions
diff --git a/src/ca/XmlConfig.cpp b/src/ca/XmlConfig.cpp
new file mode 100644
index 00000000..c12b2bc2
--- /dev/null
+++ b/src/ca/XmlConfig.cpp
@@ -0,0 +1,1099 @@
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 `XmlConfig`.`XmlConfig`, `XmlConfig`.`File`, `XmlConfig`.`ElementPath`, `XmlConfig`.`VerifyPath`, `XmlConfig`.`Name`, "
34 L"`XmlConfig`.`Value`, `XmlConfig`.`Flags`, `XmlConfig`.`Component_`, `Component`.`Attributes` "
35 L"FROM `XmlConfig`,`Component` WHERE `XmlConfig`.`Component_`=`Component`.`Component` ORDER BY `File`, `Sequence`";
36enum eXmlConfigQuery { xfqXmlConfig = 1, xfqFile, 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 pwzElementPath;
48 LPWSTR pwzVerifyPath;
49 WCHAR wzName[MAX_DARWIN_COLUMN];
50 LPWSTR pwzValue;
51 BOOL fInstalledFile;
52
53 int iXmlFlags;
54 int iCompAttributes;
55
56 XML_CONFIG_CHANGE* pxfcAdditionalChanges;
57 int cAdditionalChanges;
58
59 XML_CONFIG_CHANGE* pxfcPrev;
60 XML_CONFIG_CHANGE* pxfcNext;
61};
62
63static HRESULT FreeXmlConfigChangeList(
64 __in_opt XML_CONFIG_CHANGE* pxfcList
65 )
66{
67 HRESULT hr = S_OK;
68
69 XML_CONFIG_CHANGE* pxfcDelete;
70 while(pxfcList)
71 {
72 pxfcDelete = pxfcList;
73 pxfcList = pxfcList->pxfcNext;
74
75 if (pxfcDelete->pwzElementPath)
76 {
77 hr = MemFree(pxfcDelete->pwzElementPath);
78 ExitOnFailure(hr, "failed to free xml file element path in change list item");
79 }
80
81 if (pxfcDelete->pwzVerifyPath)
82 {
83 hr = MemFree(pxfcDelete->pwzVerifyPath);
84 ExitOnFailure(hr, "failed to free xml file verify path in change list item");
85 }
86
87 if (pxfcDelete->pwzValue)
88 {
89 hr = MemFree(pxfcDelete->pwzValue);
90 ExitOnFailure(hr, "failed to free xml file value in change list item");
91 }
92
93 hr = MemFree(pxfcDelete);
94 ExitOnFailure(hr, "failed to free xml file change list item");
95 }
96
97LExit:
98 return hr;
99}
100
101static HRESULT AddXmlConfigChangeToList(
102 __inout XML_CONFIG_CHANGE** ppxfcHead,
103 __inout XML_CONFIG_CHANGE** ppxfcTail
104 )
105{
106 Assert(ppxfcHead && ppxfcTail);
107
108 HRESULT hr = S_OK;
109
110 XML_CONFIG_CHANGE* pxfc = static_cast<XML_CONFIG_CHANGE*>(MemAlloc(sizeof(XML_CONFIG_CHANGE), TRUE));
111 ExitOnNull(pxfc, hr, E_OUTOFMEMORY, "failed to allocate memory for new xml file change list element");
112
113 // Add it to the end of the list
114 if (NULL == *ppxfcHead)
115 {
116 *ppxfcHead = pxfc;
117 *ppxfcTail = pxfc;
118 }
119 else
120 {
121 Assert(*ppxfcTail && (*ppxfcTail)->pxfcNext == NULL);
122 (*ppxfcTail)->pxfcNext = pxfc;
123 pxfc->pxfcPrev = *ppxfcTail;
124 *ppxfcTail = pxfc;
125 }
126
127LExit:
128 return hr;
129}
130
131
132static HRESULT ReadXmlConfigTable(
133 __inout XML_CONFIG_CHANGE** ppxfcHead,
134 __inout XML_CONFIG_CHANGE** ppxfcTail
135 )
136{
137 Assert(ppxfcHead && ppxfcTail);
138
139 HRESULT hr = S_OK;
140 UINT er = ERROR_SUCCESS;
141
142 PMSIHANDLE hView = NULL;
143 PMSIHANDLE hRec = NULL;
144
145 LPWSTR pwzData = NULL;
146
147 // loop through all the xml configurations
148 hr = WcaOpenExecuteView(vcsXmlConfigQuery, &hView);
149 ExitOnFailure(hr, "failed to open view on XmlConfig table");
150
151 while (S_OK == (hr = WcaFetchRecord(hView, &hRec)))
152 {
153 hr = AddXmlConfigChangeToList(ppxfcHead, ppxfcTail);
154 ExitOnFailure(hr, "failed to add xml file change to list");
155
156 // Get record Id
157 hr = WcaGetRecordString(hRec, xfqXmlConfig, &pwzData);
158 ExitOnFailure(hr, "failed to get XmlConfig record Id");
159 hr = StringCchCopyW((*ppxfcTail)->wzId, countof((*ppxfcTail)->wzId), pwzData);
160 ExitOnFailure(hr, "failed to copy XmlConfig record Id");
161
162 // Get component name
163 hr = WcaGetRecordString(hRec, xfqComponent, &pwzData);
164 ExitOnFailure(hr, "failed to get component name for XmlConfig: %ls", (*ppxfcTail)->wzId);
165
166 // Get the component's state
167 if (0 < lstrlenW(pwzData))
168 {
169 hr = StringCchCopyW((*ppxfcTail)->wzComponent, countof((*ppxfcTail)->wzComponent), pwzData);
170 ExitOnFailure(hr, "failed to copy component id");
171
172 er = ::MsiGetComponentStateW(WcaGetInstallHandle(), (*ppxfcTail)->wzComponent, &(*ppxfcTail)->isInstalled, &(*ppxfcTail)->isAction);
173 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to get install state for component id");
174 }
175
176 // Get the xml file
177 hr = WcaGetRecordFormattedString(hRec, xfqFile, &pwzData);
178 ExitOnFailure(hr, "failed to get xml file for XmlConfig: %ls", (*ppxfcTail)->wzId);
179 hr = StringCchCopyW((*ppxfcTail)->wzFile, countof((*ppxfcTail)->wzFile), pwzData);
180 ExitOnFailure(hr, "failed to copy xml file path");
181
182 // Figure out if the file is already on the machine or if it's being installed
183 hr = WcaGetRecordString(hRec, xfqFile, &pwzData);
184 ExitOnFailure(hr, "failed to get xml file for XmlConfig: %ls", (*ppxfcTail)->wzId);
185 if (NULL != wcsstr(pwzData, L"[!") || NULL != wcsstr(pwzData, L"[#"))
186 {
187 (*ppxfcTail)->fInstalledFile = TRUE;
188 }
189
190 // Get the XmlConfig table flags
191 hr = WcaGetRecordInteger(hRec, xfqXmlFlags, &(*ppxfcTail)->iXmlFlags);
192 ExitOnFailure(hr, "failed to get XmlConfig flags for XmlConfig: %ls", (*ppxfcTail)->wzId);
193
194 // Get the Element Path
195 hr = WcaGetRecordFormattedString(hRec, xfqElementPath, &(*ppxfcTail)->pwzElementPath);
196 ExitOnFailure(hr, "failed to get Element Path for XmlConfig: %ls", (*ppxfcTail)->wzId);
197
198 // Get the Verify Path
199 hr = WcaGetRecordFormattedString(hRec, xfqVerifyPath, &(*ppxfcTail)->pwzVerifyPath);
200 ExitOnFailure(hr, "failed to get Verify Path for XmlConfig: %ls", (*ppxfcTail)->wzId);
201
202 // Get the name
203 hr = WcaGetRecordFormattedString(hRec, xfqName, &pwzData);
204 ExitOnFailure(hr, "failed to get Name for XmlConfig: %ls", (*ppxfcTail)->wzId);
205 hr = StringCchCopyW((*ppxfcTail)->wzName, countof((*ppxfcTail)->wzName), pwzData);
206 ExitOnFailure(hr, "failed to copy name of element");
207
208 // Get the value
209 hr = WcaGetRecordFormattedString(hRec, xfqValue, &pwzData);
210 ExitOnFailure(hr, "failed to get Value for XmlConfig: %ls", (*ppxfcTail)->wzId);
211 hr = StrAllocString(&(*ppxfcTail)->pwzValue, pwzData, 0);
212 ExitOnFailure(hr, "failed to allocate buffer for value");
213
214 // Get the component attributes
215 hr = WcaGetRecordInteger(hRec, xfqCompAttributes, &(*ppxfcTail)->iCompAttributes);
216 ExitOnFailure(hr, "failed to get component attributes for XmlConfig: %ls", (*ppxfcTail)->wzId);
217 }
218
219 // if we looped through all records all is well
220 if (E_NOMOREITEMS == hr)
221 {
222 hr = S_OK;
223 }
224 ExitOnFailure(hr, "failed while looping through all objects to secure");
225
226LExit:
227 ReleaseStr(pwzData);
228
229 return hr;
230}
231
232static HRESULT ProcessChanges(
233 __inout XML_CONFIG_CHANGE** ppxfcHead
234 )
235{
236 Assert(ppxfcHead && *ppxfcHead);
237 HRESULT hr = S_OK;
238
239 XML_CONFIG_CHANGE* pxfc = NULL;
240 XML_CONFIG_CHANGE* pxfcNext = NULL;
241 XML_CONFIG_CHANGE* pxfcCheck = NULL;
242 int cAdditionalChanges = 0;
243 XML_CONFIG_CHANGE* pxfcLast = NULL;
244
245 // If there's only one item in the list, none of this matters
246 if (pxfc && !pxfc->pxfcNext)
247 {
248 ExitFunction();
249 }
250
251 // Loop through the list
252 pxfc = *ppxfcHead;
253 while (pxfc)
254 {
255 // Keep track of where our next spot will be since our current node may be moved
256 pxfcNext = pxfc->pxfcNext;
257
258 // With each node, check to see if it's element path matches the Id of some other node in the list
259 pxfcCheck = *ppxfcHead;
260 while (pxfcCheck)
261 {
262 if (0 == lstrcmpW(pxfc->pwzElementPath, pxfcCheck->wzId) && 0 == pxfc->iXmlFlags
263 && XMLCONFIG_CREATE & pxfcCheck->iXmlFlags && XMLCONFIG_ELEMENT & pxfcCheck->iXmlFlags)
264 {
265 // We found a match. First, take it out of the current list
266 if (pxfc->pxfcPrev)
267 {
268 pxfc->pxfcPrev->pxfcNext = pxfc->pxfcNext;
269 }
270 else // it was the head. Update the head
271 {
272 *ppxfcHead = pxfc->pxfcNext;
273 }
274
275 if (pxfc->pxfcNext)
276 {
277 pxfc->pxfcNext->pxfcPrev = pxfc->pxfcPrev;
278 }
279
280 pxfc->pxfcNext = NULL;
281 pxfc->pxfcPrev = NULL;
282
283 // Now, add this node to the end of the matched node's additional changes list
284 if (!pxfcCheck->pxfcAdditionalChanges)
285 {
286 pxfcCheck->pxfcAdditionalChanges = pxfc;
287 pxfcCheck->cAdditionalChanges = 1;
288 }
289 else
290 {
291 pxfcLast = pxfcCheck->pxfcAdditionalChanges;
292 cAdditionalChanges = 1;
293 while (pxfcLast->pxfcNext)
294 {
295 pxfcLast = pxfcLast->pxfcNext;
296 ++cAdditionalChanges;
297 }
298 pxfcLast->pxfcNext = pxfc;
299 pxfc->pxfcPrev = pxfcLast;
300 pxfcCheck->cAdditionalChanges = ++cAdditionalChanges;
301 }
302 }
303
304 pxfcCheck = pxfcCheck->pxfcNext;
305 }
306
307 pxfc = pxfcNext;
308 }
309
310LExit:
311
312 return hr;
313}
314
315
316static HRESULT BeginChangeFile(
317 __in LPCWSTR pwzFile,
318 __in int iCompAttributes,
319 __inout LPWSTR* ppwzCustomActionData
320 )
321{
322 Assert(pwzFile && *pwzFile && ppwzCustomActionData);
323
324 HRESULT hr = S_OK;
325 BOOL fIs64Bit = iCompAttributes & msidbComponentAttributes64bit;
326
327 LPBYTE pbData = NULL;
328 DWORD cbData = 0;
329
330 LPWSTR pwzRollbackCustomActionData = NULL;
331
332 if (fIs64Bit)
333 {
334 hr = WcaWriteIntegerToCaData((int)xaOpenFilex64, ppwzCustomActionData);
335 ExitOnFailure(hr, "failed to write 64-bit file indicator to custom action data");
336 }
337 else
338 {
339 hr = WcaWriteIntegerToCaData((int)xaOpenFile, ppwzCustomActionData);
340 ExitOnFailure(hr, "failed to write file indicator to custom action data");
341 }
342
343 hr = WcaWriteStringToCaData(pwzFile, ppwzCustomActionData);
344 ExitOnFailure(hr, "failed to write file to custom action data: %ls", pwzFile);
345
346 // If the file already exits, then we have to put it back the way it was on failure
347 if (FileExistsEx(pwzFile, NULL))
348 {
349 hr = FileRead(&pbData, &cbData, pwzFile);
350 ExitOnFailure(hr, "failed to read file: %ls", pwzFile);
351
352 // Set up the rollback for this file
353 hr = WcaWriteIntegerToCaData((int)fIs64Bit, &pwzRollbackCustomActionData);
354 ExitOnFailure(hr, "failed to write component bitness to rollback custom action data");
355
356 hr = WcaWriteStringToCaData(pwzFile, &pwzRollbackCustomActionData);
357 ExitOnFailure(hr, "failed to write file name to rollback custom action data: %ls", pwzFile);
358
359 hr = WcaWriteStreamToCaData(pbData, cbData, &pwzRollbackCustomActionData);
360 ExitOnFailure(hr, "failed to write file contents to rollback custom action data.");
361
362 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlConfigRollback"), pwzRollbackCustomActionData, COST_XMLFILE);
363 ExitOnFailure(hr, "failed to schedule ExecXmlConfigRollback for file: %ls", pwzFile);
364
365 ReleaseStr(pwzRollbackCustomActionData);
366 }
367LExit:
368 ReleaseMem(pbData);
369
370 return hr;
371}
372
373
374static HRESULT WriteChangeData(
375 __in XML_CONFIG_CHANGE* pxfc,
376 __in eXmlAction action,
377 __inout LPWSTR* ppwzCustomActionData
378 )
379{
380 Assert(pxfc && ppwzCustomActionData);
381
382 HRESULT hr = S_OK;
383 XML_CONFIG_CHANGE* pxfcAdditionalChanges = NULL;
384
385 hr = WcaWriteStringToCaData(pxfc->pwzElementPath, ppwzCustomActionData);
386 ExitOnFailure(hr, "failed to write ElementPath to custom action data: %ls", pxfc->pwzElementPath);
387
388 hr = WcaWriteStringToCaData(pxfc->pwzVerifyPath, ppwzCustomActionData);
389 ExitOnFailure(hr, "failed to write VerifyPath to custom action data: %ls", pxfc->pwzVerifyPath);
390
391 hr = WcaWriteStringToCaData(pxfc->wzName, ppwzCustomActionData);
392 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
393
394 hr = WcaWriteStringToCaData(pxfc->pwzValue, ppwzCustomActionData);
395 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
396
397 if (pxfc->iXmlFlags & XMLCONFIG_CREATE && pxfc->iXmlFlags & XMLCONFIG_ELEMENT && xaCreateElement == action && pxfc->pxfcAdditionalChanges)
398 {
399 hr = WcaWriteIntegerToCaData(pxfc->cAdditionalChanges, ppwzCustomActionData);
400 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
401
402 pxfcAdditionalChanges = pxfc->pxfcAdditionalChanges;
403 while (pxfcAdditionalChanges)
404 {
405 Assert((0 == lstrcmpW(pxfcAdditionalChanges->wzComponent, pxfc->wzComponent)) && 0 == pxfcAdditionalChanges->iXmlFlags && (0 == lstrcmpW(pxfcAdditionalChanges->wzFile, pxfc->wzFile)));
406
407 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->wzName, ppwzCustomActionData);
408 ExitOnFailure(hr, "failed to write Name to custom action data: %ls", pxfc->wzName);
409
410 hr = WcaWriteStringToCaData(pxfcAdditionalChanges->pwzValue, ppwzCustomActionData);
411 ExitOnFailure(hr, "failed to write Value to custom action data: %ls", pxfc->pwzValue);
412
413 pxfcAdditionalChanges = pxfcAdditionalChanges->pxfcNext;
414 }
415 }
416 else
417 {
418 hr = WcaWriteIntegerToCaData(0, ppwzCustomActionData);
419 ExitOnFailure(hr, "failed to write additional changes value to custom action data");
420 }
421
422LExit:
423 return hr;
424}
425
426
427/******************************************************************
428 SchedXmlConfig - entry point for XmlConfig Custom Action
429
430********************************************************************/
431extern "C" UINT __stdcall SchedXmlConfig(
432 __in MSIHANDLE hInstall
433 )
434{
435// AssertSz(FALSE, "debug SchedXmlConfig");
436
437 HRESULT hr = S_OK;
438 UINT er = ERROR_SUCCESS;
439
440 LPWSTR pwzCurrentFile = NULL;
441 BOOL fCurrentFileChanged = FALSE;
442
443 PMSIHANDLE hView = NULL;
444 PMSIHANDLE hRec = NULL;
445
446 XML_CONFIG_CHANGE* pxfcHead = NULL;
447 XML_CONFIG_CHANGE* pxfcTail = NULL; // TODO: do we need this any more?
448 XML_CONFIG_CHANGE* pxfc = NULL;
449
450 eXmlAction xa = xaUnknown;
451 eXmlPreserveDate xd;
452
453 LPWSTR pwzCustomActionData = NULL;
454
455 DWORD cFiles = 0;
456
457 // initialize
458 hr = WcaInitialize(hInstall, "SchedXmlConfig");
459 ExitOnFailure(hr, "failed to initialize");
460
461 hr = ReadXmlConfigTable(&pxfcHead, &pxfcTail);
462 MessageExitOnFailure(hr, msierrXmlConfigFailedRead, "failed to read XmlConfig table");
463
464 hr = ProcessChanges(&pxfcHead);
465 ExitOnFailure(hr, "failed to process XmlConfig changes");
466
467 // loop through all the xml configurations
468 for (pxfc = pxfcHead; pxfc; pxfc = pxfc->pxfcNext)
469 {
470 // If this is a different file, or the first file...
471 if (NULL == pwzCurrentFile || 0 != lstrcmpW(pwzCurrentFile, pxfc->wzFile))
472 {
473 // Remember the file we're currently working on
474 hr = StrAllocString(&pwzCurrentFile, pxfc->wzFile, 0);
475 ExitOnFailure(hr, "failed to copy file name");
476
477 fCurrentFileChanged = TRUE;
478 }
479
480 //
481 // Figure out what action to take
482 //
483 xa = xaUnknown;
484
485 // If it's being installed or reinstalled or uninstalled and that matches
486 // what we are doing then calculate the right action.
487 if ((XMLCONFIG_INSTALL & pxfc->iXmlFlags && (WcaIsInstalling(pxfc->isInstalled, pxfc->isAction) || WcaIsReInstalling(pxfc->isInstalled, pxfc->isAction))) ||
488 (XMLCONFIG_UNINSTALL & pxfc->iXmlFlags && WcaIsUninstalling(pxfc->isInstalled, pxfc->isAction)))
489 {
490 if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
491 {
492 xa = xaCreateElement;
493 }
494 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_ELEMENT & pxfc->iXmlFlags)
495 {
496 xa = xaDeleteElement;
497 }
498 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
499 {
500 xa = xaDeleteValue;
501 }
502 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_VALUE & pxfc->iXmlFlags)
503 {
504 xa = xaWriteValue;
505 }
506 else if (XMLCONFIG_CREATE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
507 {
508 xa = xaWriteDocument;
509 }
510 else if (XMLCONFIG_DELETE & pxfc->iXmlFlags && XMLCONFIG_DOCUMENT & pxfc->iXmlFlags)
511 {
512 hr = E_INVALIDARG;
513 ExitOnFailure(hr, "Invalid flag configuration. Cannot delete a fragment node.");
514 }
515 }
516
517 if (XMLCONFIG_PRESERVE_MODIFIED & pxfc->iXmlFlags)
518 {
519 xd = xdPreserve;
520 }
521 else
522 {
523 xd= xdDontPreserve;
524 }
525
526 if (xaUnknown != xa)
527 {
528 if (fCurrentFileChanged)
529 {
530 hr = BeginChangeFile(pwzCurrentFile, pxfc->iCompAttributes, &pwzCustomActionData);
531 ExitOnFailure(hr, "failed to begin file change for file: %ls", pwzCurrentFile);
532
533 fCurrentFileChanged = FALSE;
534 ++cFiles;
535 }
536
537 hr = WcaWriteIntegerToCaData((int)xa, &pwzCustomActionData);
538 ExitOnFailure(hr, "failed to write action indicator custom action data");
539
540 hr = WcaWriteIntegerToCaData((int)xd, &pwzCustomActionData);
541 ExitOnFailure(hr, "failed to write Preserve Date indicator to custom action data");
542
543 hr = WriteChangeData(pxfc, xa, &pwzCustomActionData);
544 ExitOnFailure(hr, "failed to write change data");
545 }
546 }
547
548 // If we looped through all records all is well
549 if (E_NOMOREITEMS == hr)
550 {
551 hr = S_OK;
552 }
553 ExitOnFailure(hr, "failed while looping through all objects to secure");
554
555 // Schedule the custom action and add to progress bar
556 if (pwzCustomActionData && *pwzCustomActionData)
557 {
558 Assert(0 < cFiles);
559
560 hr = WcaDoDeferredAction(PLATFORM_DECORATION(L"ExecXmlConfig"), pwzCustomActionData, cFiles * COST_XMLFILE);
561 ExitOnFailure(hr, "failed to schedule ExecXmlConfig action");
562 }
563
564LExit:
565 ReleaseStr(pwzCurrentFile);
566 ReleaseStr(pwzCustomActionData);
567
568 FreeXmlConfigChangeList(pxfcHead);
569
570 if (FAILED(hr))
571 {
572 er = ERROR_INSTALL_FAILURE;
573 }
574 return WcaFinalize(er);
575}
576
577
578/******************************************************************
579 ExecXmlConfig - entry point for XmlConfig Custom Action
580
581*******************************************************************/
582extern "C" UINT __stdcall ExecXmlConfig(
583 __in MSIHANDLE hInstall
584 )
585{
586 //AssertSz(FALSE, "debug ExecXmlConfig");
587 HRESULT hr = S_OK;
588 HRESULT hrOpenFailure = S_OK;
589 UINT er = ERROR_SUCCESS;
590
591 BOOL fIsWow64Process = FALSE;
592 BOOL fIsFSRedirectDisabled = FALSE;
593 BOOL fPreserveDate = FALSE;
594
595 LPWSTR pwzCustomActionData = NULL;
596 LPWSTR pwzData = NULL;
597 LPWSTR pwzFile = NULL;
598 LPWSTR pwzElementPath = NULL;
599 LPWSTR pwzVerifyPath = NULL;
600 LPWSTR pwzName = NULL;
601 LPWSTR pwzValue = NULL;
602 LPWSTR pwz = NULL;
603 int cAdditionalChanges = 0;
604
605 IXMLDOMDocument* pixd = NULL;
606 IXMLDOMNode* pixn = NULL;
607 IXMLDOMNode* pixnVerify = NULL;
608 IXMLDOMNode* pixnNewNode = NULL;
609 IXMLDOMNode* pixnRemovedChild = NULL;
610
611 IXMLDOMDocument* pixdNew = NULL;
612 IXMLDOMElement* pixeNew = NULL;
613
614 FILETIME ft;
615
616 int id = IDRETRY;
617
618 eXmlAction xa;
619 eXmlPreserveDate xd;
620
621 // initialize
622 hr = WcaInitialize(hInstall, "ExecXmlConfig");
623 ExitOnFailure(hr, "failed to initialize");
624
625 hr = XmlInitialize();
626 ExitOnFailure(hr, "failed to initialize xml utilities");
627
628 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
629 ExitOnFailure(hr, "failed to get CustomActionData");
630
631 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
632
633 pwz = pwzCustomActionData;
634
635 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
636 ExitOnFailure(hr, "failed to process CustomActionData");
637
638 // Initialize the Wow64 API - store the result in fWow64APIPresent
639 // If it fails, this doesn't warrant an error yet, because we only need the Wow64 API in some cases
640 WcaInitializeWow64();
641 fIsWow64Process = WcaIsWow64Process();
642
643 if (xaOpenFile != xa && xaOpenFilex64 != xa)
644 {
645 ExitOnFailure(hr = E_INVALIDARG, "invalid custom action data");
646 }
647
648 // loop through all the passed in data
649 while (pwz && *pwz)
650 {
651 hr = WcaReadStringFromCaData(&pwz, &pwzFile);
652 ExitOnFailure(hr, "failed to read file name from custom action data");
653
654 // Default to not preserve date, preserve it if any modifications require us to
655 fPreserveDate = FALSE;
656
657 // Open the file
658 ReleaseNullObject(pixd);
659
660 if (xaOpenFilex64 == xa)
661 {
662 if (!fIsWow64Process)
663 {
664 hr = E_NOTIMPL;
665 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but the custom action process is not running in WOW.");
666 }
667
668 hr = WcaDisableWow64FSRedirection();
669 ExitOnFailure(hr, "Custom action was told to act on a 64-bit component, but was unable to disable filesystem redirection through the Wow64 API.");
670
671 fIsFSRedirectDisabled = TRUE;
672 }
673
674 hr = XmlLoadDocumentFromFileEx(pwzFile, XML_LOAD_PRESERVE_WHITESPACE, &pixd);
675 if (FAILED(hr))
676 {
677 // 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.
678 hrOpenFailure = hr;
679 hr = S_OK;
680 }
681 else
682 {
683 hrOpenFailure = S_OK;
684 }
685
686 WcaLog(LOGMSG_VERBOSE, "Configuring Xml File: %ls", pwzFile);
687
688 while (pwz && *pwz)
689 {
690 // If we skip past an element that has additional changes we need to strip them off the stream before
691 // moving on to the next element. Do that now and then restart the outer loop.
692 if (cAdditionalChanges > 0)
693 {
694 while (cAdditionalChanges > 0)
695 {
696 hr = WcaReadStringFromCaData(&pwz, &pwzName);
697 ExitOnFailure(hr, "failed to process CustomActionData");
698 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
699 ExitOnFailure(hr, "failed to process CustomActionData");
700
701 cAdditionalChanges--;
702 }
703 continue;
704 }
705
706 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xa);
707 ExitOnFailure(hr, "failed to process CustomActionData");
708
709 // Break if we need to move on to a different file
710 if (xaOpenFile == xa || xaOpenFilex64 == xa)
711 {
712 break;
713 }
714
715 hr = WcaReadIntegerFromCaData(&pwz, (int*) &xd);
716 ExitOnFailure(hr, "failed to process CustomActionData");
717
718 if (xdPreserve == xd)
719 {
720 fPreserveDate = TRUE;
721 }
722
723 // Get path, name, and value to be written
724 hr = WcaReadStringFromCaData(&pwz, &pwzElementPath);
725 ExitOnFailure(hr, "failed to process CustomActionData");
726 hr = WcaReadStringFromCaData(&pwz, &pwzVerifyPath);
727 ExitOnFailure(hr, "failed to process CustomActionData");
728 hr = WcaReadStringFromCaData(&pwz, &pwzName);
729 ExitOnFailure(hr, "failed to process CustomActionData");
730 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
731 ExitOnFailure(hr, "failed to process CustomActionData");
732 hr = WcaReadIntegerFromCaData(&pwz, &cAdditionalChanges);
733 ExitOnFailure(hr, "failed to process CustomActionData");
734
735 // 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.
736 if (FAILED(hrOpenFailure))
737 {
738 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
739 {
740 MessageExitOnFailure(hr = hrOpenFailure, msierrXmlConfigFailedOpen, "failed to load XML file: %ls", pwzFile);
741 }
742 else
743 {
744 continue;
745 }
746 }
747
748 // Select the node we're about to modify
749 ReleaseNullObject(pixn);
750
751 hr = XmlSelectSingleNode(pixd, pwzElementPath, &pixn);
752
753 // 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.
754 if (S_FALSE == hr)
755 {
756 if (xaCreateElement == xa || xaWriteValue == xa || xaWriteDocument == xa)
757 {
758 hr = HRESULT_FROM_WIN32(ERROR_OBJECT_NOT_FOUND);
759 }
760 else
761 {
762 hr = S_OK;
763 continue;
764 }
765 }
766
767 MessageExitOnFailure(hr, msierrXmlConfigFailedSelect, "failed to find node: %ls in XML file: %ls", pwzElementPath, pwzFile);
768
769 // Make the modification
770 switch (xa)
771 {
772 case xaWriteValue:
773 if (pwzName && *pwzName)
774 {
775 // We're setting an attribute
776 hr = XmlSetAttribute(pixn, pwzName, pwzValue);
777 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
778 }
779 else
780 {
781 // We're setting the text of the node
782 hr = XmlSetText(pixn, pwzValue);
783 ExitOnFailure(hr, "failed to set text to: %ls for element %ls. Make sure that XPath points to an element.", pwzValue, pwzElementPath);
784 }
785 break;
786 case xaWriteDocument:
787 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
788 {
789 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
790 if (S_OK == hr)
791 {
792 // We found the verify path which means we have no further work to do
793 continue;
794 }
795 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
796 }
797
798 hr = XmlLoadDocumentEx(pwzValue, XML_LOAD_PRESERVE_WHITESPACE, &pixdNew);
799 ExitOnFailure(hr, "Failed to load value as document.");
800
801 hr = pixdNew->get_documentElement(&pixeNew);
802 ExitOnFailure(hr, "Failed to get document element.");
803
804 hr = pixn->appendChild(pixeNew, NULL);
805 ExitOnFailure(hr, "Failed to append document element on to parent element.");
806
807 ReleaseNullObject(pixeNew);
808 ReleaseNullObject(pixdNew);
809 break;
810
811 case xaCreateElement:
812 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
813 {
814 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
815 if (S_OK == hr)
816 {
817 // We found the verify path which means we have no further work to do
818 continue;
819 }
820 ExitOnFailure(hr, "failed to query verify path: %ls", pwzVerifyPath);
821 }
822
823 hr = XmlCreateChild(pixn, pwzName, &pixnNewNode);
824 ExitOnFailure(hr, "failed to create child element: %ls", pwzName);
825
826 if (pwzValue && *pwzValue)
827 {
828 hr = XmlSetText(pixnNewNode, pwzValue);
829 ExitOnFailure(hr, "failed to set text to: %ls for node: %ls", pwzValue, pwzName);
830 }
831
832 while (cAdditionalChanges > 0)
833 {
834 hr = WcaReadStringFromCaData(&pwz, &pwzName);
835 ExitOnFailure(hr, "failed to process CustomActionData");
836 hr = WcaReadStringFromCaData(&pwz, &pwzValue);
837 ExitOnFailure(hr, "failed to process CustomActionData");
838
839 // Set the additional attribute
840 hr = XmlSetAttribute(pixnNewNode, pwzName, pwzValue);
841 ExitOnFailure(hr, "failed to set attribute: %ls to value %ls", pwzName, pwzValue);
842
843 cAdditionalChanges--;
844 }
845
846 ReleaseNullObject(pixnNewNode);
847 break;
848 case xaDeleteValue:
849 if (pwzName && *pwzName)
850 {
851 // Delete the attribute
852 hr = XmlRemoveAttribute(pixn, pwzName);
853 ExitOnFailure(hr, "failed to remove attribute: %ls", pwzName);
854 }
855 else
856 {
857 // Clear the text value for the node
858 hr = XmlSetText(pixn, L"");
859 ExitOnFailure(hr, "failed to clear text value");
860 }
861 break;
862 case xaDeleteElement:
863 if (NULL != pwzVerifyPath && 0 != pwzVerifyPath[0])
864 {
865 hr = XmlSelectSingleNode(pixn, pwzVerifyPath, &pixnVerify);
866 if (S_OK == hr)
867 {
868 hr = pixn->removeChild(pixnVerify, &pixnRemovedChild);
869 ExitOnFailure(hr, "failed to remove created child element");
870
871 ReleaseNullObject(pixnRemovedChild);
872 }
873 else
874 {
875 WcaLog(LOGMSG_VERBOSE, "Failed to select path %ls for deleting. Skipping...", pwzVerifyPath);
876 hr = S_OK;
877 }
878 }
879 else
880 {
881 // TODO: This requires a VerifyPath to delete an element. Should we support not having one?
882 WcaLog(LOGMSG_VERBOSE, "No VerifyPath specified for delete element of ID: %ls", pwzElementPath);
883 }
884 break;
885 default:
886 ExitOnFailure(hr = E_UNEXPECTED, "Invalid modification specified in custom action data");
887 break;
888 }
889 }
890
891
892 // Now that we've made all of the changes to this file, save it and move on to the next
893 if (S_OK == hrOpenFailure)
894 {
895 if (fPreserveDate)
896 {
897 hr = FileGetTime(pwzFile, NULL, NULL, &ft);
898 ExitOnFailure(hr, "failed to get modified time of file : %ls", pwzFile);
899 }
900
901 int iSaveAttempt = 0;
902
903 do
904 {
905 hr = XmlSaveDocument(pixd, pwzFile);
906 if (FAILED(hr))
907 {
908 id = WcaErrorMessage(msierrXmlConfigFailedSave, hr, INSTALLMESSAGE_ERROR | MB_ABORTRETRYIGNORE, 1, pwzFile);
909 switch (id)
910 {
911 case IDABORT:
912 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
913 case IDRETRY:
914 hr = S_FALSE; // hit me, baby, one more time
915 break;
916 case IDIGNORE:
917 hr = S_OK; // pretend everything is okay and bail
918 break;
919 case 0: // No UI case, MsiProcessMessage returns 0
920 if (STIERR_SHARING_VIOLATION == hr)
921 {
922 // Only in case of sharing violation do we retry 30 times, once a second.
923 if (iSaveAttempt < 30)
924 {
925 hr = S_FALSE;
926 ++iSaveAttempt;
927 WcaLog(LOGMSG_VERBOSE, "Unable to save changes to XML file: %ls, retry attempt: %x", pwzFile, iSaveAttempt);
928 Sleep(1000);
929 }
930 else
931 {
932 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
933 }
934 }
935 break;
936 default: // Unknown error
937 ExitOnFailure(hr, "Failed to save changes to XML file: %ls", pwzFile);
938 }
939 }
940 } while (S_FALSE == hr);
941
942 if (fPreserveDate)
943 {
944 hr = FileSetTime(pwzFile, NULL, NULL, &ft);
945 ExitOnFailure(hr, "failed to set modified time of file : %ls", pwzFile);
946 }
947
948 if (fIsFSRedirectDisabled)
949 {
950 fIsFSRedirectDisabled = FALSE;
951 WcaRevertWow64FSRedirection();
952 }
953 }
954 }
955
956LExit:
957 // Make sure we revert FS Redirection if necessary before exiting
958 if (fIsFSRedirectDisabled)
959 {
960 fIsFSRedirectDisabled = FALSE;
961 WcaRevertWow64FSRedirection();
962 }
963 WcaFinalizeWow64();
964
965 ReleaseStr(pwzCustomActionData);
966 ReleaseStr(pwzData);
967 ReleaseStr(pwzFile);
968 ReleaseStr(pwzElementPath);
969 ReleaseStr(pwzVerifyPath);
970 ReleaseStr(pwzName);
971 ReleaseStr(pwzValue);
972
973 ReleaseObject(pixeNew);
974 ReleaseObject(pixdNew);
975
976 ReleaseObject(pixn);
977 ReleaseObject(pixd);
978 ReleaseObject(pixnNewNode);
979 ReleaseObject(pixnRemovedChild);
980
981 XmlUninitialize();
982
983 if (FAILED(hr))
984 {
985 er = ERROR_INSTALL_FAILURE;
986 }
987 return WcaFinalize(er);
988}
989
990
991/******************************************************************
992 ExecXmlConfigRollback - entry point for XmlConfig rollback Custom Action
993
994*******************************************************************/
995extern "C" UINT __stdcall ExecXmlConfigRollback(
996 __in MSIHANDLE hInstall
997 )
998{
999// AssertSz(FALSE, "debug ExecXmlConfigRollback");
1000 HRESULT hr = S_OK;
1001 UINT er = ERROR_SUCCESS;
1002
1003 int iIs64Bit;
1004 BOOL fIs64Bit = FALSE;
1005
1006 LPWSTR pwzCustomActionData = NULL;
1007 LPWSTR pwz = NULL;
1008 LPWSTR pwzFileName = NULL;
1009 LPBYTE pbData = NULL;
1010 DWORD_PTR cbData = 0;
1011 DWORD cbDataWritten = 0;
1012
1013 FILETIME ft;
1014
1015 HANDLE hFile = INVALID_HANDLE_VALUE;
1016
1017 // initialize
1018 hr = WcaInitialize(hInstall, "ExecXmlConfigRollback");
1019 ExitOnFailure(hr, "failed to initialize");
1020
1021
1022 hr = WcaGetProperty( L"CustomActionData", &pwzCustomActionData);
1023 ExitOnFailure(hr, "failed to get CustomActionData");
1024
1025 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzCustomActionData);
1026
1027 pwz = pwzCustomActionData;
1028
1029 hr = WcaReadIntegerFromCaData(&pwz, &iIs64Bit);
1030 ExitOnFailure(hr, "failed to read component bitness from custom action data");
1031
1032 hr = WcaReadStringFromCaData(&pwz, &pwzFileName);
1033 ExitOnFailure(hr, "failed to read file name from custom action data");
1034
1035 hr = WcaReadStreamFromCaData(&pwz, &pbData, &cbData);
1036 ExitOnFailure(hr, "failed to read file contents from custom action data");
1037
1038 fIs64Bit = (BOOL)iIs64Bit;
1039
1040 if (fIs64Bit)
1041 {
1042 hr = WcaInitializeWow64();
1043 if (S_FALSE == hr)
1044 {
1045 hr = TYPE_E_DLLFUNCTIONNOTFOUND;
1046 }
1047 ExitOnFailure(hr, "failed to initialize Wow64 API");
1048
1049 if (!WcaIsWow64Process())
1050 {
1051 hr = E_NOTIMPL;
1052 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but the Wow64 API is unavailable.");
1053 }
1054
1055 hr = WcaDisableWow64FSRedirection();
1056 ExitOnFailure(hr, "Custom action was told to rollback a 64-bit component, but was unable to Disable Filesystem Redirection through the Wow64 API.");
1057 }
1058
1059 hr = FileGetTime(pwzFileName, NULL, NULL, &ft);
1060 ExitOnFailure(hr, "Failed to get modified date of file %ls.", pwzFileName);
1061
1062 // Open the file
1063 hFile = ::CreateFileW(pwzFileName, GENERIC_WRITE, NULL, NULL, TRUNCATE_EXISTING, NULL, NULL);
1064 ExitOnInvalidHandleWithLastError(hFile, hr, "failed to open file: %ls", pwzFileName);
1065
1066 // Write out the old data
1067 if (!::WriteFile(hFile, pbData, (DWORD)cbData, &cbDataWritten, NULL))
1068 {
1069 ExitOnLastError(hr, "failed to write to file: %ls", pwzFileName);
1070 }
1071
1072 Assert(cbData == cbDataWritten);
1073
1074 ReleaseFile(hFile);
1075
1076 hr = FileSetTime(pwzFileName, NULL, NULL, &ft);
1077 ExitOnFailure(hr, "Failed to set modified date of file %ls.", pwzFileName);
1078
1079LExit:
1080 ReleaseStr(pwzCustomActionData);
1081 ReleaseStr(pwzFileName);
1082
1083 ReleaseFile(hFile);
1084
1085 if (fIs64Bit)
1086 {
1087 WcaRevertWow64FSRedirection();
1088 WcaFinalizeWow64();
1089 }
1090
1091 ReleaseMem(pbData);
1092
1093 if (FAILED(hr))
1094 {
1095 er = ERROR_INSTALL_FAILURE;
1096 }
1097 return WcaFinalize(er);
1098}
1099