main
cpp 448 lines 16.4 KB
Raw
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 typedef struct _DETECT_AUTHENTICATION_REQUIRED_DATA
6 {
7 BURN_USER_EXPERIENCE* pUX;
8 LPCWSTR wzPackageOrContainerId;
9 } DETECT_AUTHENTICATION_REQUIRED_DATA;
10
11 // internal function definitions
12 static 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
20 static HRESULT DetectAtomFeedUpdate(
21 __in_z LPCWSTR wzBundleId,
22 __in BURN_USER_EXPERIENCE* pUX,
23 __in BURN_UPDATE* pUpdate
24 );
25
26 static 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
35 extern "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->fDetectedForeignProviderKeyBundleId = FALSE;
43 pRegistration->fSelfRegisteredAsDependent = FALSE;
44 pRegistration->fParentRegisteredAsDependent = FALSE;
45 pRegistration->fForwardCompatibleBundleExists = FALSE;
46 pRegistration->fEligibleForCleanup = FALSE;
47
48 if (pRegistration->rgDependents)
49 {
50 ReleaseDependencyArray(pRegistration->rgDependents, pRegistration->cDependents);
51 }
52 pRegistration->rgDependents = NULL;
53 pRegistration->cDependents = 0;
54
55 for (DWORD iPackage = 0; iPackage < pPackages->cPackages; ++iPackage)
56 {
57 BURN_PACKAGE* pPackage = pPackages->rgPackages + iPackage;
58
59 pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_UNKNOWN;
60 pPackage->cacheRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
61 pPackage->installRegistrationState = BURN_PACKAGE_REGISTRATION_STATE_UNKNOWN;
62
63 pPackage->fCached = FALSE;
64
65 if (BURN_PACKAGE_TYPE_MSI == pPackage->type)
66 {
67 pPackage->Msi.operation = BOOTSTRAPPER_RELATED_OPERATION_NONE;
68
69 for (DWORD iFeature = 0; iFeature < pPackage->Msi.cFeatures; ++iFeature)
70 {
71 BURN_MSIFEATURE* pFeature = pPackage->Msi.rgFeatures + iFeature;
72
73 pFeature->currentState = BOOTSTRAPPER_FEATURE_STATE_UNKNOWN;
74 }
75
76 for (DWORD iSlipstreamMsp = 0; iSlipstreamMsp < pPackage->Msi.cSlipstreamMspPackages; ++iSlipstreamMsp)
77 {
78 BURN_SLIPSTREAM_MSP* pSlipstreamMsp = pPackage->Msi.rgSlipstreamMsps + iSlipstreamMsp;
79
80 pSlipstreamMsp->dwMsiChainedPatchIndex = BURN_PACKAGE_INVALID_PATCH_INDEX;
81 }
82
83 ReleaseNullMem(pPackage->Msi.rgChainedPatches);
84 pPackage->Msi.cChainedPatches = 0;
85 }
86 else if (BURN_PACKAGE_TYPE_MSP == pPackage->type)
87 {
88 ReleaseNullMem(pPackage->Msp.rgTargetProducts);
89 pPackage->Msp.cTargetProductCodes = 0;
90 }
91
92 for (DWORD iProvider = 0; iProvider < pPackage->cDependencyProviders; ++iProvider)
93 {
94 BURN_DEPENDENCY_PROVIDER* pProvider = pPackage->rgDependencyProviders + iProvider;
95
96 pProvider->fExists = FALSE;
97 pProvider->fBundleRegisteredAsDependent = FALSE;
98
99 if (pProvider->rgDependents)
100 {
101 ReleaseDependencyArray(pProvider->rgDependents, pProvider->cDependents);
102 }
103 pProvider->rgDependents = NULL;
104 pProvider->cDependents = 0;
105 }
106
107 PackageUninitializeCompatible(&pPackage->compatiblePackage);
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
118 extern "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->fDetectedForeignProviderKeyBundleId)
127 {
128 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
129 {
130 BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
131
132 if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->detectRelationType &&
133 CSTR_EQUAL == ::CompareStringW(LOCALE_NEUTRAL, NORM_IGNORECASE, pRegistration->sczDetectedProviderKeyBundleId, -1, pRelatedBundle->package.sczId, -1))
134 {
135 hr = VerCompareParsedVersions(pRegistration->pVersion, pRelatedBundle->pVersion, &nCompareResult);
136 ExitOnFailure(hr, "Failed to compare bundle version '%ls' to related bundle version '%ls'", pRegistration->pVersion->sczVersion, pRelatedBundle->pVersion->sczVersion);
137
138 if (nCompareResult <= 0)
139 {
140 if (pRelatedBundle->fPlannable)
141 {
142 pRelatedBundle->fForwardCompatible = TRUE;
143 pRegistration->fForwardCompatibleBundleExists = TRUE;
144 }
145
146 hr = BACallbackOnDetectForwardCompatibleBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->detectRelationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached);
147 ExitOnRootFailure(hr, "BA aborted detect forward compatible bundle.");
148
149 LogId(REPORT_STANDARD, MSG_DETECTED_FORWARD_COMPATIBLE_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->detectRelationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached));
150 }
151 }
152 }
153 }
154
155 LExit:
156 return hr;
157 }
158
159 extern "C" HRESULT DetectReportRelatedBundles(
160 __in BURN_USER_EXPERIENCE* pUX,
161 __in BURN_REGISTRATION* pRegistration,
162 __in BOOTSTRAPPER_RELATION_TYPE relationType,
163 __out BOOL* pfEligibleForCleanup
164 )
165 {
166 HRESULT hr = S_OK;
167 BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE planRelationType = BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_NONE;
168 BOOTSTRAPPER_REQUEST_STATE uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
169 *pfEligibleForCleanup = BOOTSTRAPPER_REGISTRATION_TYPE_NONE != pRegistration->detectedRegistrationType || pRegistration->fCached;
170
171 for (DWORD iRelatedBundle = 0; iRelatedBundle < pRegistration->relatedBundles.cRelatedBundles; ++iRelatedBundle)
172 {
173 const BURN_RELATED_BUNDLE* pRelatedBundle = pRegistration->relatedBundles.rgRelatedBundles + iRelatedBundle;
174
175 LogId(REPORT_STANDARD, MSG_DETECTED_RELATED_BUNDLE, pRelatedBundle->package.sczId, LoggingRelationTypeToString(pRelatedBundle->detectRelationType), LoggingPerMachineToString(pRelatedBundle->package.fPerMachine), pRelatedBundle->pVersion->sczVersion, LoggingBoolToString(pRelatedBundle->package.fCached));
176
177 hr = BACallbackOnDetectRelatedBundle(pUX, pRelatedBundle->package.sczId, pRelatedBundle->detectRelationType, pRelatedBundle->sczTag, pRelatedBundle->package.fPerMachine, pRelatedBundle->pVersion, !pRelatedBundle->package.fCached);
178 ExitOnRootFailure(hr, "BA aborted detect related bundle.");
179
180 // For now, if any related bundles will be executed during uninstall by default then never automatically clean up the bundle.
181 if (*pfEligibleForCleanup && pRelatedBundle->fPlannable)
182 {
183 planRelationType = BOOTSTRAPPER_RELATED_BUNDLE_PLAN_TYPE_NONE;
184 uninstallRequestState = BOOTSTRAPPER_REQUEST_STATE_NONE;
185
186 hr = PlanDefaultRelatedBundlePlanType(pRelatedBundle->detectRelationType, pRegistration->pVersion, pRelatedBundle->pVersion, &planRelationType);
187 ExitOnFailure(hr, "Failed to get the default plan type for related bundle for calculating fEligibleForCleanup");
188
189 hr = PlanDefaultRelatedBundleRequestState(relationType, planRelationType, BOOTSTRAPPER_ACTION_UNINSTALL, &uninstallRequestState);
190 ExitOnFailure(hr, "Failed to get the default request state for related bundle for calculating fEligibleForCleanup");
191
192 if (BOOTSTRAPPER_REQUEST_STATE_NONE != uninstallRequestState)
193 {
194 *pfEligibleForCleanup = FALSE;
195 }
196 }
197 }
198
199 LExit:
200 return hr;
201 }
202
203 extern "C" HRESULT DetectUpdate(
204 __in_z LPCWSTR wzBundleId,
205 __in BURN_USER_EXPERIENCE* pUX,
206 __in BURN_UPDATE* pUpdate
207 )
208 {
209 HRESULT hr = S_OK;
210 BOOL fBeginCalled = FALSE;
211 BOOL fSkip = TRUE;
212 BOOL fIgnoreError = FALSE;
213 LPWSTR sczOriginalSource = NULL;
214
215 // If no update source was specified, skip update detection.
216 if (!pUpdate->sczUpdateSource || !*pUpdate->sczUpdateSource)
217 {
218 ExitFunction();
219 }
220
221 fBeginCalled = TRUE;
222
223 hr = StrAllocString(&sczOriginalSource, pUpdate->sczUpdateSource, 0);
224 ExitOnFailure(hr, "Failed to duplicate update feed source.");
225
226 hr = BACallbackOnDetectUpdateBegin(pUX, sczOriginalSource, &fSkip);
227 ExitOnRootFailure(hr, "BA aborted detect update begin.");
228
229 if (!fSkip)
230 {
231 hr = DetectAtomFeedUpdate(wzBundleId, pUX, pUpdate);
232 ExitOnFailure(hr, "Failed to detect atom feed update.");
233 }
234
235 LExit:
236 ReleaseStr(sczOriginalSource);
237
238 if (fBeginCalled)
239 {
240 BACallbackOnDetectUpdateComplete(pUX, hr, &fIgnoreError);
241 if (fIgnoreError)
242 {
243 hr = S_OK;
244 }
245 }
246
247 return hr;
248 }
249
250 static HRESULT WINAPI AuthenticationRequired(
251 __in LPVOID pData,
252 __in HINTERNET hUrl,
253 __in long lHttpCode,
254 __out BOOL* pfRetrySend,
255 __out BOOL* pfRetry
256 )
257 {
258 Assert(401 == lHttpCode || 407 == lHttpCode);
259
260 HRESULT hr = S_OK;
261 DWORD er = ERROR_SUCCESS;
262 BOOTSTRAPPER_ERROR_TYPE errorType = (401 == lHttpCode) ? BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_SERVER : BOOTSTRAPPER_ERROR_TYPE_HTTP_AUTH_PROXY;
263 LPWSTR sczError = NULL;
264 DETECT_AUTHENTICATION_REQUIRED_DATA* pAuthenticationData = reinterpret_cast<DETECT_AUTHENTICATION_REQUIRED_DATA*>(pData);
265 int nResult = IDNOACTION;
266
267 *pfRetrySend = FALSE;
268 *pfRetry = FALSE;
269
270 hr = StrAllocFromError(&sczError, HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), NULL);
271 ExitOnFailure(hr, "Failed to allocation error string.");
272
273 BACallbackOnError(pAuthenticationData->pUX, errorType, pAuthenticationData->wzPackageOrContainerId, ERROR_ACCESS_DENIED, sczError, MB_RETRYCANCEL, 0, NULL, &nResult); // ignore return value.
274 nResult = BootstrapperApplicationCheckExecuteResult(pAuthenticationData->pUX, FALSE, BURN_MB_RETRYTRYAGAIN, nResult);
275 if (IDTRYAGAIN == nResult && pAuthenticationData->pUX->hwndDetect)
276 {
277 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);
278 if (ERROR_SUCCESS == er || ERROR_CANCELLED == er)
279 {
280 hr = HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
281 }
282 else if (ERROR_INTERNET_FORCE_RETRY == er)
283 {
284 *pfRetrySend = TRUE;
285 hr = S_OK;
286 }
287 else
288 {
289 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
290 }
291 }
292 else if (IDRETRY == nResult)
293 {
294 *pfRetry = TRUE;
295 hr = S_OK;
296 }
297 else
298 {
299 hr = HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
300 }
301
302 LExit:
303 ReleaseStr(sczError);
304
305 return hr;
306 }
307
308 static HRESULT DownloadUpdateFeed(
309 __in_z LPCWSTR wzBundleId,
310 __in BURN_USER_EXPERIENCE* pUX,
311 __in BURN_UPDATE* pUpdate,
312 __deref_inout_z LPWSTR* psczTempFile
313 )
314 {
315 HRESULT hr = S_OK;
316 DOWNLOAD_SOURCE downloadSource = { };
317 DOWNLOAD_CACHE_CALLBACK cacheCallback = { };
318 DOWNLOAD_AUTHENTICATION_CALLBACK authenticationCallback = { };
319 DETECT_AUTHENTICATION_REQUIRED_DATA authenticationData = { };
320 LPWSTR sczUpdateId = NULL;
321 LPWSTR sczError = NULL;
322 DWORD64 qwDownloadSize = 0;
323
324 // Always do our work in the working folder, even if cached.
325 hr = PathCreateTimeBasedTempFile(NULL, L"UpdateFeed", NULL, L"xml", psczTempFile, NULL);
326 ExitOnFailure(hr, "Failed to create UpdateFeed based on current system time.");
327
328 // 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
329 hr = StrAllocString(&downloadSource.sczUrl, pUpdate->sczUpdateSource, 0);
330 ExitOnFailure(hr, "Failed to copy update url.");
331
332 cacheCallback.pfnProgress = NULL; //UpdateProgressRoutine;
333 cacheCallback.pfnCancel = NULL; // TODO: set this
334 cacheCallback.pv = NULL; //pProgress;
335
336 authenticationData.pUX = pUX;
337 authenticationData.wzPackageOrContainerId = wzBundleId;
338
339 authenticationCallback.pv = static_cast<LPVOID>(&authenticationData);
340 authenticationCallback.pfnAuthenticate = &AuthenticationRequired;
341
342 hr = DownloadUrl(&downloadSource, qwDownloadSize, *psczTempFile, &cacheCallback, &authenticationCallback);
343 ExitOnFailure(hr, "Failed attempt to download update feed from URL: '%ls' to: '%ls'", downloadSource.sczUrl, *psczTempFile);
344
345 LExit:
346 if (FAILED(hr))
347 {
348 if (*psczTempFile)
349 {
350 FileEnsureDelete(*psczTempFile);
351 }
352
353 ReleaseNullStr(*psczTempFile);
354 }
355
356 ReleaseStr(downloadSource.sczUrl);
357 ReleaseStr(downloadSource.sczUser);
358 ReleaseStr(downloadSource.sczPassword);
359 ReleaseStr(downloadSource.sczAuthorizationHeader);
360 ReleaseStr(sczUpdateId);
361 ReleaseStr(sczError);
362 return hr;
363 }
364
365
366 static HRESULT DetectAtomFeedUpdate(
367 __in_z LPCWSTR wzBundleId,
368 __in BURN_USER_EXPERIENCE* pUX,
369 __in BURN_UPDATE* pUpdate
370 )
371 {
372 Assert(pUpdate && pUpdate->sczUpdateSource && *pUpdate->sczUpdateSource);
373 #ifdef DEBUG
374 LogStringLine(REPORT_STANDARD, "DetectAtomFeedUpdate() - update location: %ls", pUpdate->sczUpdateSource);
375 #endif
376
377
378 HRESULT hr = S_OK;
379 LPWSTR sczUpdateFeedTempFile = NULL;
380 ATOM_FEED* pAtomFeed = NULL;
381 APPLICATION_UPDATE_CHAIN* pApupChain = NULL;
382 BOOL fStopProcessingUpdates = FALSE;
383 LPWSTR sczHash = NULL;
384
385 hr = AtomInitialize();
386 ExitOnFailure(hr, "Failed to initialize Atom.");
387
388 hr = DownloadUpdateFeed(wzBundleId, pUX, pUpdate, &sczUpdateFeedTempFile);
389 ExitOnFailure(hr, "Failed to download update feed.");
390
391 hr = AtomParseFromFile(sczUpdateFeedTempFile, &pAtomFeed);
392 ExitOnFailure(hr, "Failed to parse update atom feed: %ls.", sczUpdateFeedTempFile);
393
394 hr = ApupAllocChainFromAtom(pAtomFeed, &pApupChain);
395 ExitOnFailure(hr, "Failed to allocate update chain from atom feed.");
396
397 if (0 < pApupChain->cEntries)
398 {
399 for (DWORD i = 0; i < pApupChain->cEntries; ++i)
400 {
401 APPLICATION_UPDATE_ENTRY* pAppUpdateEntry = &pApupChain->rgEntries[i];
402 APPLICATION_UPDATE_ENCLOSURE* pEnclosure = pAppUpdateEntry->rgEnclosures;
403 LPCWSTR wzHash = L"";
404 BOOTSTRAPPER_UPDATE_HASH_TYPE hashType = BOOTSTRAPPER_UPDATE_HASH_TYPE_NONE;
405
406 if (pEnclosure && pEnclosure->rgbDigest && APUP_HASH_ALGORITHM_SHA512 == pEnclosure->digestAlgorithm)
407 {
408 hr = StrAllocHexEncode(pEnclosure->rgbDigest, pEnclosure->cbDigest, &sczHash);
409 ExitOnFailure(hr, "Failed to encode hash as string.");
410
411 wzHash = sczHash;
412 hashType = BOOTSTRAPPER_UPDATE_HASH_TYPE_SHA512;
413 }
414
415 hr = BACallbackOnDetectUpdate(pUX,
416 pEnclosure ? pEnclosure->wzUrl : NULL,
417 pEnclosure ? pEnclosure->dw64Size : 0,
418 wzHash,
419 hashType,
420 pAppUpdateEntry->pVersion,
421 pAppUpdateEntry->wzTitle,
422 pAppUpdateEntry->wzSummary,
423 pAppUpdateEntry->wzContentType,
424 pAppUpdateEntry->wzContent,
425 &fStopProcessingUpdates);
426 ExitOnRootFailure(hr, "BA aborted detect update.");
427
428 if (fStopProcessingUpdates)
429 {
430 break;
431 }
432 }
433 }
434
435 LExit:
436 if (sczUpdateFeedTempFile && *sczUpdateFeedTempFile)
437 {
438 FileEnsureDelete(sczUpdateFeedTempFile);
439 }
440
441 ApupFreeChain(pApupChain);
442 AtomFreeFeed(pAtomFeed);
443 ReleaseStr(sczUpdateFeedTempFile);
444 ReleaseStr(sczHash);
445 AtomUninitialize();
446
447 return hr;
448 }