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