aboutsummaryrefslogtreecommitdiff
path: root/src/engine/detect.cpp
diff options
context:
space:
mode:
authorSean Hall <r.sean.hall@gmail.com>2018-12-29 22:12:08 -0600
committerSean Hall <r.sean.hall@gmail.com>2018-12-29 22:12:08 -0600
commit61847dddd4fd497057c780658e383c4627de19ec (patch)
treef85a845182922538ab9aa6ee85b0db3ab40c1f6e /src/engine/detect.cpp
parent8295f5f8fd28042e1a0a172d5afbba79178064c2 (diff)
downloadwix-61847dddd4fd497057c780658e383c4627de19ec.tar.gz
wix-61847dddd4fd497057c780658e383c4627de19ec.tar.bz2
wix-61847dddd4fd497057c780658e383c4627de19ec.zip
Import code from old v4 repo
Diffstat (limited to 'src/engine/detect.cpp')
-rw-r--r--src/engine/detect.cpp431
1 files changed, 431 insertions, 0 deletions
diff --git a/src/engine/detect.cpp b/src/engine/detect.cpp
new file mode 100644
index 00000000..7953daf5
--- /dev/null
+++ b/src/engine/detect.cpp
@@ -0,0 +1,431 @@
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 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->fEnabledForwardCompatibleBundle = FALSE;
43 PackageUninitialize(&pRegistration->forwardCompatibleBundle);
44
45 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
46 {
47 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
48
49 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
50
51 pPackage->cache = BURN_CACHE_STATE_NONE;
52 for (DWORD iPayload = 0; iPayload < pPackage->cPayloads; ++iPayload)
53 {
54 BURN_PACKAGE_PAYLOAD* pPayload = pPackage->rgPayloads + iPayload;
55 pPayload->fCached = FALSE;
56 }
57
58 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
59 {
60 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
61 {
62 BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
63
64 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
65 }
66
67 pPackage->Msi.fCompatibleInstalled = FALSE;
68 }
69 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
70 {
71 ReleaseNullMem(pPackage->Msp.rgTargetProducts);
72 pPackage->Msp.cTargetProductCodes = 0;
73 }
74 }
75
76 for (DWORD iPatchInfo = 0; iPatchInfo < pPackages->cPatchInfo; ++iPatchInfo)
77 {
78 MSIPATCHSEQUENCEINFOW* pPatchInfo = pPackages->rgPatchInfo + iPatchInfo;
79 pPatchInfo->dwOrder = 0;
80 pPatchInfo->uStatus = 0;
81 }
82}
83
84extern "C" HRESULT DetectForwardCompatibleBundle(
85 __in BURN_USER_EXPERIENCE* pUX,
86 __in BOOTSTRAPPER_COMMAND* pCommand,
87 __in BURN_REGISTRATION* pRegistration
88 )
89{
90 HRESULT hr = S_OK;
91 BOOL fRecommendIgnore = TRUE;
92 BOOL fIgnoreBundle = FALSE;
93
94 if (pRegistration->sczDetectedProviderKeyBundleId &&
95 CSTR_EQUAL != ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRegistration->sczId, -1))
96 {
97 // Only change the recommendation if an active parent was provided.
98 if (pRegistration->sczActiveParent && *pRegistration->sczActiveParent)
99 {
100 // On install, recommend running the forward compatible bundle because there is an active parent. This
101 // will essentially register the parent with the forward compatible bundle.
102 if (BOOTSTRAPPER_ACTION_INSTALL == pCommand->action)
103 {
104 fRecommendIgnore = FALSE;
105 }
106 else if (BOOTSTRAPPER_ACTION_UNINSTALL == pCommand->action ||
107 BOOTSTRAPPER_ACTION_MODIFY == pCommand->action ||
108 BOOTSTRAPPER_ACTION_REPAIR == pCommand->action)
109 {
110 // When modifying the bundle, only recommend running the forward compatible bundle if the parent
111 // is already registered as a dependent of the provider key.
112 if (DependencyDependentExists(pRegistration, pRegistration->sczActiveParent))
113 {
114 fRecommendIgnore = FALSE;
115 }
116 }
117 }
118
119 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
120 {
121 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
122 fIgnoreBundle = fRecommendIgnore;
123
124 if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->relationType &&
125 pRegistration->qwVersion <= pRelatedBundle->qwVersion &&
126 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1))
127 {
128 hr = UserExperienceOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, &fIgnoreBundle);
129 ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle.");
130
131 if (!fIgnoreBundle)
132 {
133 hr = PseudoBundleInitializePassthrough(&pRegistration->forwardCompatibleBundle, pCommand, NULL, pRegistration->sczActiveParent, pRegistration->sczAncestors, &pRelatedBundle->package);
134 ExitOnFailure(hr, "Failed to initialize update bundle.");
135
136 pRegistration->fEnabledForwardCompatibleBundle = TRUE;
137 }
138
139 LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingBoolToString(pRegistration->fEnabledForwardCompatibleBundle));
140 break;
141 }
142 }
143 }
144
145LExit:
146 return hr;
147}
148
149extern "C" HRESULT DetectReportRelatedBundles(
150 __in BURN_USER_EXPERIENCE* pUX,
151 __in BURN_REGISTRATION* pRegistration,
152 __in BOOTSTRAPPER_RELATION_TYPE relationType,
153 __in BOOTSTRAPPER_ACTION action
154 )
155{
156 HRESULT hr = S_OK;
157
158 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
159 {
160 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
161 BOOTSTRAPPER_RELATED_OPERATION operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
162
163 switch (pRelatedBundle->relationType)
164 {
165 case BOOTSTRAPPER_RELATION_UPGRADE:
166 if (BOOTSTRAPPER_RELATION_UPGRADE != relationType && BOOTSTRAPPER_ACTION_UNINSTALL < action)
167 {
168 if (pRegistration->qwVersion > pRelatedBundle->qwVersion)
169 {
170 operation = BOOTSTRAPPER_RELATED_OPERATION_MAJOR_UPGRADE;
171 }
172 else if (pRegistration->qwVersion < pRelatedBundle->qwVersion)
173 {
174 operation = BOOTSTRAPPER_RELATED_OPERATION_DOWNGRADE;
175 }
176 }
177 break;
178
179 case BOOTSTRAPPER_RELATION_PATCH: __fallthrough;
180 case BOOTSTRAPPER_RELATION_ADDON:
181 if (BOOTSTRAPPER_ACTION_UNINSTALL == action)
182 {
183 operation = BOOTSTRAPPER_RELATED_OPERATION_REMOVE;
184 }
185 else if (BOOTSTRAPPER_ACTION_INSTALL == action || BOOTSTRAPPER_ACTION_MODIFY == action)
186 {
187 operation = BOOTSTRAPPER_RELATED_OPERATION_INSTALL;
188 }
189 else if (BOOTSTRAPPER_ACTION_REPAIR == action)
190 {
191 operation = BOOTSTRAPPER_RELATED_OPERATION_REPAIR;
192 }
193 break;
194
195 case BOOTSTRAPPER_RELATION_DETECT: __fallthrough;
196 case BOOTSTRAPPER_RELATION_DEPENDENT:
197 break;
198
199 default:
200 hr = E_FAIL;
201 ExitOnRootFailure(hr, "Unexpected relation type encountered: %d", pRelatedBundle->relationType);
202 break;
203 }
204
205 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->relationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), LoggingVersionToString(pRelatedBundle->qwVersion), LoggingRelatedOperationToString(operation));
206
207 hr = UserExperienceOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->relationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->qwVersion, operation);
208 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
209 }
210
211LExit:
212 return hr;
213}
214
215extern "C" HRESULT DetectUpdate(
216 __in_z LPCWSTR wzBundleId,
217 __in BURN_USER_EXPERIENCE* pUX,
218 __in BURN_UPDATE* pUpdate
219 )
220{
221 HRESULT hr = S_OK;
222 BOOL fBeginCalled = FALSE;
223 BOOL fSkip = TRUE;
224 BOOL fIgnoreError = FALSE;
225
226 // If no update source was specified, skip update detection.
227 if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource)
228 {
229 ExitFunction();
230 }
231
232 fBeginCalled = TRUE;
233 hr = UserExperienceOnDetectUpdateBegin(pUX, pUpdate->sczUpdateSource, &fSkip);
234 ExitOnRootFailure(hr, "BA aborted detect update begin.");
235
236 if (!fSkip)
237 {
238 hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate);
239 ExitOnFailure(hr, "Failed to detect atom feed update.");
240 }
241
242LExit:
243 if (fBeginCalled)
244 {
245 UserExperienceOnDetectUpdateComplete(pUX, hr, &fIgnoreError);
246 if (fIgnoreError)
247 {
248 hr = S_OK;
249 }
250 }
251
252 return hr;
253}
254
255static HRESULT AuthenticationRequired(
256 __in LPVOID pData,
257 __in HINTERNET hUrl,
258 __in long lHttpCode,
259 __out BOOL* pfRetrySend,
260 __out BOOL* pfRetry
261 )
262{
263 Assert(401 == lHttpCode || 407 == lHttpCode);
264
265 HRESULT hr = S_OK;
266 DWORD er = ERROR_SUCCESS;
267 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
268 LPWSTR sczError = NULL;
269 DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData);
270 int nResult = IDNOACTION;
271
272 *pfRetrySend = FALSE;
273 *pfRetry = FALSE;
274
275 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
276 ExitOnFailure(hr, "Failed to allocation error string.");
277
278 UserExperienceOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYTRYAGAIN, 0, NULL, &nResult); // ignore return value.
279 nResult = UserExperienceCheckExecuteResult(pAuthenticationData->pUX, FALSE, MB_RETRYTRYAGAIN, nResult);
280 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
281 {
282 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);
283 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
284 {
285 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
286 }
287 else if (ERROR_INTERNET_FORCE_RETRY == er)
288 {
289 *pfRetrySend = TRUE;
290 hr = S_OK;
291 }
292 else
293 {
294 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
295 }
296 }
297 else if (IDRETRY == nResult)
298 {
299 *pfRetry = TRUE;
300 hr = S_OK;
301 }
302 else
303 {
304 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
305 }
306
307LExit:
308 ReleaseStr(sczError);
309
310 return hr;
311}
312
313static HRESULT DownloadUpdateFeed(
314 __in_z LPCWSTR wzBundleId,
315 __in BURN_USER_EXPERIENCE* pUX,
316 __in BURN_UPDATE* pUpdate,
317 __deref_inout_z LPWSTR* psczTempFile
318 )
319{
320 HRESULT hr = S_OK;
321 DOWNLOAD_SOURCE downloadSource = { };
322 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
323 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
324 DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
325 LPWSTR sczUpdateId = NULL;
326 LPWSTR sczError = NULL;
327 DWORD64 qwDownloadSize = 0;
328
329 // Always do our work in the working folder, even if cached.
330 hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL);
331 ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time.");
332
333 // 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
334 hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0);
335 ExitOnFailure(hr, "Failed to copy update url.");
336
337 cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine;
338 cacheCallback.pfnCancel = NULL; // TODO: set this
339 cacheCallback.pv = NULL; //pProgress;
340
341 authenticationData.pUX = pUX;
342 authenticationData.wzPackageOrContainerId = wzBundleId;
343
344 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
345 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
346
347 hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback);
348 ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile);
349
350LExit:
351 if (FAILED(hr))
352 {
353 if (*psczTempFile)
354 {
355 FileEnsureDelete(*psczTempFile);
356 }
357
358 ReleaseNullStr(*psczTempFile);
359 }
360
361 ReleaseStr(downloadSource.sczUrl);
362 ReleaseStr(downloadSource.sczUser);
363 ReleaseStr(downloadSource.sczPassword);
364 ReleaseStr(sczUpdateId);
365 ReleaseStr(sczError);
366 return hr;
367}
368
369
370static HRESULT DetectAtomFeedUpdate(
371 __in_z LPCWSTR wzBundleId,
372 __in BURN_USER_EXPERIENCE* pUX,
373 __in BURN_UPDATE* pUpdate
374 )
375{
376 Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource);
377#ifdef DEBUG
378 LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource);
379#endif
380
381
382 HRESULT hr = S_OK;
383 LPWSTR sczUpdateFeedTempFile = NULL;
384 ATOM_FEED* pAtomFeed = NULL;
385 APPLICATION_UPDATE_CHAIN* pApupChain = NULL;
386 BOOL fStopProcessingUpdates = FALSE;
387
388 hr = AtomInitialize();
389 ExitOnFailure(hr, "Failed to initialize Atom.");
390
391 hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile);
392 ExitOnFailure(hr, "Failed to download update feed.");
393
394 hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed);
395 ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile);
396
397 hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain);
398 ExitOnFailure(hr, "Failed to allocate update chain from atom feed.");
399
400 if (0 < pApupChain->cEntries)
401 {
402 for (DWORD i = 0; i < pApupChain->cEntries; ++i)
403 {
404 APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i];
405
406 hr = UserExperienceOnDetectUpdate(pUX, pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->wzUrl : NULL,
407 pAppUpdateEntry->rgEnclosures ? pAppUpdateEntry->rgEnclosures->dw64Size : 0,
408 pAppUpdateEntry->dw64Version, pAppUpdateEntry->wzTitle,
409 pAppUpdateEntry->wzSummary, pAppUpdateEntry->wzContentType, pAppUpdateEntry->wzContent, &fStopProcessingUpdates);
410 ExitOnRootFailure(hr, "BA aborted detect update.");
411
412 if (fStopProcessingUpdates)
413 {
414 break;
415 }
416 }
417 }
418
419LExit:
420 if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile)
421 {
422 FileEnsureDelete(sczUpdateFeedTempFile);
423 }
424
425 ApupFreeChain(pApupChain);
426 AtomFreeFeed(pAtomFeed);
427 ReleaseStr(sczUpdateFeedTempFile);
428 AtomUninitialize();
429
430 return hr;
431}