aboutsummaryrefslogtreecommitdiff
path: root/src/ext/Util/ca/XmlConfig.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext/Util/ca/XmlConfig.cpp')
-rw-r--r--src/ext/Util/ca/XmlConfig.cpp1130
1 files changed, 1130 insertions, 0 deletions
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}