aboutsummaryrefslogtreecommitdiff
path: root/src/burn/engine/detect.cpp
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2021-04-22 17:06:54 -0700
committerRob Mensching <rob@firegiant.com>2021-04-29 16:36:06 -0700
commitaf10c45d7b3a44af0b461a557847fe03263dcc10 (patch)
tree6a5c1532304782c36ffe4200b38f3afb76789a43 /src/burn/engine/detect.cpp
parent9c2aed97299fb96aeee3f1471ce40225437aaecf (diff)
downloadwix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.gz
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.tar.bz2
wix-af10c45d7b3a44af0b461a557847fe03263dcc10.zip
Move burn into burn
Diffstat (limited to 'src/burn/engine/detect.cpp')
-rw-r--r--src/burn/engine/detect.cpp469
1 files changed, 469 insertions, 0 deletions
diff --git a/src/burn/engine/detect.cpp b/src/burn/engine/detect.cpp
new file mode 100644
index 00000000..dc35e747
--- /dev/null
+++ b/src/burn/engine/detect.cpp
@@ -0,0 +1,469 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3#include "precomp.h"
4
5typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA
6{
7 BURN_USER_EXPERIENCE* pUX;
8 LPCWSTR wzPackageOrContainerId;
9} DETECT_AUTHENTICATION_REQUIRED_DATA;
10
11// internal function definitions
12static HRESULT WINAPI AuthenticationRequired(
13 __in LPVOID pData,
14 __in HINTERNET hUrl,
15 __in long lHttpCode,
16 __out BOOL* pfRetrySend,
17 __out BOOL* pfRetry
18 );
19
20static HRESULT DetectAtomFeedUpdate(
21 __in_z LPCWSTR wzBundleId,
22 __in BURN_USER_EXPERIENCE* pUX,
23 __in BURN_UPDATE* pUpdate
24 );
25
26static HRESULT DownloadUpdateFeed(
27 __in_z LPCWSTR wzBundleId,
28 __in BURN_USER_EXPERIENCE* pUX,
29 __in BURN_UPDATE* pUpdate,
30 __deref_inout_z LPWSTR* psczTempFile
31 );
32
33// function definitions
34
35extern "C" void DetectReset(
36 __in BURN_REGISTRATION* pRegistration,
37 __in BURN_PACKAGES* pPackages
38 )
39{
40 RelatedBundlesUninitialize(&pRegistration->relatedBundles);
41 ReleaseNullStr(pRegistration->sczDetectedProviderKeyBundleId);
42 pRegistration->fSelfRegisteredAsDependent = FALSE;
43 pRegistration->fParentRegisteredAsDependent = FALSE;
44 pRegistration->fForwardCompatibleBundleExists = FALSE;
45 pRegistration->fEligibleForCleanup = FALSE;
46
47 if (pRegistration->rgIgnoredDependencies)
48 {
49 ReleaseDependencyArray(pRegistration->rgIgnoredDependencies, pRegistration->cIgnoredDependencies);
50 }
51 pRegistration->rgIgnoredDependencies = NULL;
52 pRegistration->cIgnoredDependencies = 0;
53
54 if (pRegistration->rgDependents)
55 {
56 ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents);
57 }
58 pRegistration->rgDependents = NULL;
59 pRegistration->cDependents = 0;
60
61 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
62 {
63 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
64
65 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
66 pPackage->fPackageProviderExists = FALSE;
67 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
68 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
69
70 pPackage->fCached = FALSE;
71
72 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
73 {
74 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
75 {
76 BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
77
78 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
79 }
80
81 for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp)
82 {
83 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp;
84
85 pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX;
86 }
87
88 ReleaseNullMem(pPackage->Msi.rgChainedPatches);
89 pPackage->Msi.cChainedPatches = 0;
90 }
91 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
92 {
93 ReleaseNullMem(pPackage->Msp.rgTargetProducts);
94 pPackage->Msp.cTargetProductCodes = 0;
95 }
96
97 for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider)
98 {
99 BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider;
100
101 if (pProvider->rgDependents)
102 {
103 ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents);
104 }
105 pProvider->rgDependents = NULL;
106 pProvider->cDependents = 0;
107 }
108 }
109
110 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
111 {
112 MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo;
113 pPatchInfo->dwOrder = 0;
114 pPatchInfo->uStatus = 0;
115 }
116}
117
118extern "C" HRESULT DetectForwardCompatibleBundles(
119 __in BURN_USER_EXPERIENCE* pUX,
120 __in BURN_REGISTRATION* pRegistration
121 )
122{
123 HRESULT hr = S_OK;
124 int nCompareResult = 0;
125
126 if (pRegistration->sczDetectedProviderKeyBundleId &&
127 CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1))
128 {
129 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
130 {
131 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
132
133 if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType &&
134 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1))
135 {
136 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
137 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
138
139 if (nCompareResult <= 0)
140 {
141 if (pRelatedBundle->fPlannable)
142 {
143 pRelatedBundle->fForwardCompatible = TRUE;
144 pRegistration->fForwardCompatibleBundleExists = TRUE;
145 }
146
147 hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached);
148 ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle.");
149
150 LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached));
151 }
152 }
153 }
154 }
155
156LExit:
157 return hr;
158}
159
160extern "C" HRESULT DetectReportRelatedBundles(
161 __in BURN_USER_EXPERIENCE* pUX,
162 __in BURN_REGISTRATION* pRegistration,
163 __in BOOTSTRAPPER_RELATION_TYPE relationType,
164 __in BOOTSTRAPPER_ACTION action,
165 __out BOOL* pfEligibleForCleanup
166 )
167{
168 HRESULT hr = S_OK;
169 int nCompareResult = 0;
170 BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
171 *pfEligibleForCleanup = pRegistration->fInstalled || pRegistration->fCached;
172
173 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
174 {
175 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
176 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
177
178 switch (pRelatedBundle->relationType)
179 {
180 case BOOTSTRAPPER_RELATION_UPGRADE:
181 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
182 {
183 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
184 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
185
186 if (nCompareResult < 0)
187 {
188 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
189 }
190 else
191 {
192 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
193 }
194 }
195 break;
196
197 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
198 case BOOTSTRAPPER_RELATION_ADDON:
199 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
200 {
201 operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE;
202 }
203 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
204 {
205 operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL;
206 }
207 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
208 {
209 operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR;
210 }
211 break;
212
213 case BOOTSTRAPPER_RELATION_DETECT: __fallthrough;
214 case BOOTSTRAPPER_RELATION_DEPENDENT:
215 break;
216
217 default:
218 hr = E_FAIL;
219 ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType);
220 break;
221 }
222
223 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingRelatedOperationToString(operation), LoggingBoolToString(pRelatedBundle->package.fCached));
224
225 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, operation, !pRelatedBundle->package.fCached);
226 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
227
228 // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle.
229 if (*pfEligibleForCleanup && pRelatedBundle->fPlannable)
230 {
231 uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
232 hr = PlanDefaultRelatedBundleRequestState(relationType, pRelatedBundle->relationType, BOOTSTRAPPER_ACTION_UNINSTALL, pRegistration->pVersion, pRelatedBundle->pVersion, &uninstallRequestState);
233 ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup");
234
235 if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState)
236 {
237 *pfEligibleForCleanup = FALSE;
238 }
239 }
240 }
241
242LExit:
243 return hr;
244}
245
246extern "C" HRESULT DetectUpdate(
247 __in_z LPCWSTR wzBundleId,
248 __in BURN_USER_EXPERIENCE* pUX,
249 __in BURN_UPDATE* pUpdate
250 )
251{
252 HRESULT hr = S_OK;
253 BOOL fBeginCalled = FALSE;
254 BOOL fSkip = TRUE;
255 BOOL fIgnoreError = FALSE;
256 LPWSTR sczOriginalSource = NULL;
257
258 // If no update source was specified, skip update detection.
259 if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource)
260 {
261 ExitFunction();
262 }
263
264 fBeginCalled = TRUE;
265
266 hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0);
267 ExitOnFailure(hr, "Failed to duplicate update feed source.");
268
269 hr = UserExperienceOnDetectUpdateBegin(pUX, sczOriginalSource, &fSkip);
270 ExitOnRootFailure(hr, "BA aborted detect update begin.");
271
272 if (!fSkip)
273 {
274 hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate);
275 ExitOnFailure(hr, "Failed to detect atom feed update.");
276 }
277
278LExit:
279 ReleaseStr(sczOriginalSource);
280
281 if (fBeginCalled)
282 {
283 UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError);
284 if (fIgnoreError)
285 {
286 hr = S_OK;
287 }
288 }
289
290 return hr;
291}
292
293static HRESULT WINAPI AuthenticationRequired(
294 __in LPVOID pData,
295 __in HINTERNET hUrl,
296 __in long lHttpCode,
297 __out BOOL* pfRetrySend,
298 __out BOOL* pfRetry
299 )
300{
301 Assert(401 == lHttpCode || 407 == lHttpCode);
302
303 HRESULT hr = S_OK;
304 DWORD er = ERROR_SUCCESS;
305 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
306 LPWSTR sczError = NULL;
307 DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData);
308 int nResult = IDNOACTION;
309
310 *pfRetrySend = FALSE;
311 *pfRetry = FALSE;
312
313 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
314 ExitOnFailure(hr, "Failed to allocation error string.");
315
316 UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value.
317 nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
318 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
319 {
320 er = ::InternetErrorDlg(pAuthenticationData->pUX->hwndDetect, hUrl, ERROR_INTERNET_INCORRECT_PASSWORD, FLAGS_ERROR_UI_FILTER_FOR_ERRORS | FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, NULL);
321 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
322 {
323 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
324 }
325 else if (ERROR_INTERNET_FORCE_RETRY == er)
326 {
327 *pfRetrySend = TRUE;
328 hr = S_OK;
329 }
330 else
331 {
332 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
333 }
334 }
335 else if (IDRETRY == nResult)
336 {
337 *pfRetry = TRUE;
338 hr = S_OK;
339 }
340 else
341 {
342 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
343 }
344
345LExit:
346 ReleaseStr(sczError);
347
348 return hr;
349}
350
351static HRESULT DownloadUpdateFeed(
352 __in_z LPCWSTR wzBundleId,
353 __in BURN_USER_EXPERIENCE* pUX,
354 __in BURN_UPDATE* pUpdate,
355 __deref_inout_z LPWSTR* psczTempFile
356 )
357{
358 HRESULT hr = S_OK;
359 DOWNLOAD_SOURCE downloadSource = { };
360 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
361 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
362 DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
363 LPWSTR sczUpdateId = NULL;
364 LPWSTR sczError = NULL;
365 DWORD64 qwDownloadSize = 0;
366
367 // Always do our work in the working folder, even if cached.
368 hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL);
369 ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time.");
370
371 // Do we need a means of the BA to pass in a user name and password? If so, we should copy it to downloadSource here
372 hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0);
373 ExitOnFailure(hr, "Failed to copy update url.");
374
375 cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine;
376 cacheCallback.pfnCancel = NULL; // TODO: set this
377 cacheCallback.pv = NULL; //pProgress;
378
379 authenticationData.pUX = pUX;
380 authenticationData.wzPackageOrContainerId = wzBundleId;
381
382 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
383 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
384
385 hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback);
386 ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile);
387
388LExit:
389 if (FAILED(hr))
390 {
391 if (*psczTempFile)
392 {
393 FileEnsureDelete(*psczTempFile);
394 }
395
396 ReleaseNullStr(*psczTempFile);
397 }
398
399 ReleaseStr(downloadSource.sczUrl);
400 ReleaseStr(downloadSource.sczUser);
401 ReleaseStr(downloadSource.sczPassword);
402 ReleaseStr(sczUpdateId);
403 ReleaseStr(sczError);
404 return hr;
405}
406
407
408static HRESULT DetectAtomFeedUpdate(
409 __in_z LPCWSTR wzBundleId,
410 __in BURN_USER_EXPERIENCE* pUX,
411 __in BURN_UPDATE* pUpdate
412 )
413{
414 Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource);
415#ifdef DEBUG
416 LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource);
417#endif
418
419
420 HRESULT hr = S_OK;
421 LPWSTR sczUpdateFeedTempFile = NULL;
422 ATOM_FEED* pAtomFeed = NULL;
423 APPLICATION_UPDATE_CHAIN* pApupChain = NULL;
424 BOOL fStopProcessingUpdates = FALSE;
425
426 hr = AtomInitialize();
427 ExitOnFailure(hr, "Failed to initialize Atom.");
428
429 hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile);
430 ExitOnFailure(hr, "Failed to download update feed.");
431
432 hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed);
433 ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile);
434
435 hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain);
436 ExitOnFailure(hr, "Failed to allocate update chain from atom feed.");
437
438 if (0 < pApupChain->cEntries)
439 {
440 for (DWORD i = 0; i < pApupChain->cEntries; ++i)
441 {
442 APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i];
443
444 hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL,
445 pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0,
446 pAppUpdateEntry->pVersion, pAppUpdateEntry->wzTitle,
447 pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates);
448 ExitOnRootFailure(hr, "BA aborted detect update.");
449
450 if (fStopProcessingUpdates)
451 {
452 break;
453 }
454 }
455 }
456
457LExit:
458 if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile)
459 {
460 FileEnsureDelete(sczUpdateFeedTempFile);
461 }
462
463 ApupFreeChain(pApupChain);
464 AtomFreeFeed(pAtomFeed);
465 ReleaseStr(sczUpdateFeedTempFile);
466 AtomUninitialize();
467
468 return hr;
469}