extern "C" HRESULT MspEngineDetectPackage( __in BURN_PACKAGE* pPackage, __in BURN_USER_EXPERIENCE* pUserExperience ) { HRESULT hr = S_OK; int nResult = IDOK; LPWSTR sczState = NULL; if (0 == pPackage->Msp.cTargetProductCodes) { pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; } else { // Start the package state at the the highest state then loop through all the // target product codes and end up setting the current state to the lowest // package state applied to the the target product codes. pPackage->currentState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; for (DWORD i = 0; i < pPackage->Msp.cTargetProductCodes; ++i) { BURN_MSPTARGETPRODUCT* pTargetProduct = pPackage->Msp.rgTargetProducts + i; hr = WiuGetPatchInfoEx(pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode, NULL, pTargetProduct->context, INSTALLPROPERTY_PATCHSTATE, &sczState); if (SUCCEEDED(hr)) { switch (*sczState) { case '1': pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_PRESENT; break; case '2': pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_SUPERSEDED; break; case '4': pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_OBSOLETE; break; default: pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; break; } } else if (HRESULT_FROM_WIN32(ERROR_UNKNOWN_PATCH) == hr) { pTargetProduct->patchPackageState = BOOTSTRAPPER_PACKAGE_STATE_ABSENT; hr = S_OK; } ExitOnFailure2(hr, "Failed to get patch information for patch code: %ls, target product code: %ls", pPackage->Msp.sczPatchCode, pTargetProduct->wzTargetProductCode); if (pPackage->currentState > pTargetProduct->patchPackageState) { pPackage->currentState = pTargetProduct->patchPackageState; } nResult = pUserExperience->pUserExperience->OnDetectTargetMsiPackage(pPackage->sczId, pTargetProduct->wzTargetProductCode, pTargetProduct->patchPackageState); hr = UserExperienceInterpretResult(pUserExperience, MB_OKCANCEL, nResult); ExitOnRootFailure(hr, "UX aborted detect target MSI package."); } } LExit: ReleaseStr(sczState); return hr; }
HRESULT ScaWebErrorRead( SCA_WEB_ERROR **ppsweList, __inout LPWSTR *ppwzCustomActionData ) { // AssertSz(0, "Debug ScaWebErrorRead here"); HRESULT hr = S_OK; MSIHANDLE hRec; LPWSTR pwzData = NULL; SCA_WEB_ERROR* pswe; WCA_WRAPQUERY_HANDLE hWrapQuery = NULL; ExitOnNull(ppsweList, hr, E_INVALIDARG, "Failed to read web error, because no web error was provided to read"); hr = WcaBeginUnwrapQuery(&hWrapQuery, ppwzCustomActionData); ExitOnFailure(hr, "Failed to unwrap query for ScaAppPoolRead"); if (0 == WcaGetQueryRecords(hWrapQuery)) { WcaLog(LOGMSG_VERBOSE, "Skipping ScaWebErrorRead() - required tables not present."); ExitFunction1(hr = S_FALSE); } // loop through all the web errors while (S_OK == (hr = WcaFetchWrappedRecord(hWrapQuery, &hRec))) { hr = AddWebErrorToList(ppsweList); ExitOnFailure(hr, "failed to add web error to list"); pswe = *ppsweList; hr = WcaGetRecordInteger(hRec, weqErrorCode, &(pswe->iErrorCode)); ExitOnFailure(hr, "failed to get IIsWebError.ErrorCode"); hr = WcaGetRecordInteger(hRec, weqSubCode, &(pswe->iSubCode)); ExitOnFailure(hr, "failed to get IIsWebError.SubCode"); hr = WcaGetRecordInteger(hRec, weqParentType, &(pswe->iParentType)); ExitOnFailure(hr, "failed to get IIsWebError.ParentType"); hr = WcaGetRecordString(hRec, weqParentValue, &pwzData); ExitOnFailure(hr, "Failed to get IIsWebError.ParentValue"); hr = ::StringCchCopyW(pswe->wzParentValue, countof(pswe->wzParentValue), pwzData); ExitOnFailure(hr, "Failed to copy IIsWebError.ParentValue"); hr = WcaGetRecordString(hRec, weqFile, &pwzData); ExitOnFailure(hr, "Failed to get IIsWebError.File"); hr = ::StringCchCopyW(pswe->wzFile, countof(pswe->wzFile), pwzData); ExitOnFailure(hr, "Failed to copy IIsWebError.File"); hr = WcaGetRecordString(hRec, weqURL, &pwzData); ExitOnFailure(hr, "Failed to get IIsWebError.URL"); hr = ::StringCchCopyW(pswe->wzURL, countof(pswe->wzURL), pwzData); ExitOnFailure(hr, "Failed to copy IIsWebError.URL"); // If they've specified both a file and a URL, that's invalid if (*(pswe->wzFile) && *(pswe->wzURL)) ExitOnFailure2(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA), "Both File and URL specified for web error. File: %ls, URL: %ls", pswe->wzFile, pswe->wzURL); } if (E_NOMOREITEMS == hr) { hr = S_OK; } ExitOnFailure(hr, "Failure while processing web errors"); LExit: WcaFinishUnwrapQuery(hWrapQuery); ReleaseStr(pwzData); return hr; }
extern "C" HRESULT MspEngineExecutePackage( __in_opt HWND hwndParent, __in BURN_EXECUTE_ACTION* pExecuteAction, __in BURN_VARIABLES* pVariables, __in BOOL fRollback, __in PFN_MSIEXECUTEMESSAGEHANDLER pfnMessageHandler, __in LPVOID pvContext, __out BOOTSTRAPPER_APPLY_RESTART* pRestart ) { HRESULT hr = S_OK; INSTALLUILEVEL uiLevel = pExecuteAction->mspTarget.pPackage->Msp.fDisplayInternalUI ? INSTALLUILEVEL_DEFAULT : static_cast<INSTALLUILEVEL>(INSTALLUILEVEL_NONE | INSTALLUILEVEL_SOURCERESONLY); WIU_MSI_EXECUTE_CONTEXT context = { }; WIU_RESTART restart = WIU_RESTART_NONE; LPWSTR sczCachedDirectory = NULL; LPWSTR sczMspPath = NULL; LPWSTR sczPatches = NULL; LPWSTR sczProperties = NULL; LPWSTR sczObfuscatedProperties = NULL; // default to "verbose" logging DWORD dwLogMode = WIU_LOG_DEFAULT | INSTALLLOGMODE_VERBOSE; // get cached MSP paths for (DWORD i = 0; i < pExecuteAction->mspTarget.cOrderedPatches; ++i) { LPCWSTR wzAppend = NULL; BURN_PACKAGE* pMspPackage = pExecuteAction->mspTarget.rgOrderedPatches[i].pPackage; AssertSz(BURN_PACKAGE_TYPE_MSP == pMspPackage->type, "Invalid package type added to ordered patches."); if (BOOTSTRAPPER_ACTION_STATE_INSTALL == pExecuteAction->mspTarget.action) { hr = CacheGetCompletedPath(pMspPackage->fPerMachine, pMspPackage->sczCacheId, &sczCachedDirectory); ExitOnFailure1(hr, "Failed to get cached path for MSP package: %ls", pMspPackage->sczId); // TODO: Figure out if this makes sense -- the variable is set to the last patch's path only // Best effort to set the execute package cache folder variable. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, sczCachedDirectory, TRUE); hr = PathConcat(sczCachedDirectory, pMspPackage->rgPayloads[0].pPayload->sczFilePath, &sczMspPath); ExitOnFailure(hr, "Failed to build MSP path."); wzAppend = sczMspPath; } else // uninstall { wzAppend = pMspPackage->Msp.sczPatchCode; } if (NULL != sczPatches) { hr = StrAllocConcat(&sczPatches, L";", 0); ExitOnFailure(hr, "Failed to semi-colon delimit patches."); } hr = StrAllocConcat(&sczPatches, wzAppend, 0); ExitOnFailure(hr, "Failed to append patch."); } // Best effort to set the execute package action variable. VariableSetNumeric(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, pExecuteAction->mspTarget.action, TRUE); // Wire up the external UI handler and logging. hr = WiuInitializeExternalUI(pfnMessageHandler, uiLevel, hwndParent, pvContext, fRollback, &context); ExitOnFailure(hr, "Failed to initialize external UI handler."); //if (BURN_LOGGING_LEVEL_DEBUG == logLevel) //{ // dwLogMode | INSTALLLOGMODE_EXTRADEBUG; //} if (pExecuteAction->mspTarget.sczLogPath && *pExecuteAction->mspTarget.sczLogPath) { hr = WiuEnableLog(dwLogMode, pExecuteAction->mspTarget.sczLogPath, 0); ExitOnFailure2(hr, "Failed to enable logging for package: %ls to: %ls", pExecuteAction->mspTarget.pPackage->sczId, pExecuteAction->mspTarget.sczLogPath); } // set up properties hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczProperties, FALSE); ExitOnFailure(hr, "Failed to add properties to argument string."); hr = MsiEngineConcatProperties(pExecuteAction->mspTarget.pPackage->Msp.rgProperties, pExecuteAction->mspTarget.pPackage->Msp.cProperties, pVariables, fRollback, &sczObfuscatedProperties, TRUE); ExitOnFailure(hr, "Failed to add properties to obfuscated argument string."); LogId(REPORT_STANDARD, MSG_APPLYING_PATCH_PACKAGE, pExecuteAction->mspTarget.pPackage->sczId, LoggingActionStateToString(pExecuteAction->mspTarget.action), sczPatches, sczObfuscatedProperties, pExecuteAction->mspTarget.sczTargetProductCode); // // Do the actual action. // switch (pExecuteAction->mspTarget.action) { case BOOTSTRAPPER_ACTION_STATE_INSTALL: __fallthrough; case BOOTSTRAPPER_ACTION_STATE_REPAIR: hr = StrAllocConcatSecure(&sczProperties, L" PATCH=\"", 0); ExitOnFailure(hr, "Failed to add PATCH property on install."); hr = StrAllocConcatSecure(&sczProperties, sczPatches, 0); ExitOnFailure(hr, "Failed to add patches to PATCH property on install."); hr = StrAllocConcatSecure(&sczProperties, L"\" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on install."); hr = WiuConfigureProductEx(pExecuteAction->mspTarget.sczTargetProductCode, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, sczProperties, &restart); ExitOnFailure(hr, "Failed to install MSP package."); break; case BOOTSTRAPPER_ACTION_STATE_UNINSTALL: hr = StrAllocConcatSecure(&sczProperties, L" REBOOT=ReallySuppress", 0); ExitOnFailure(hr, "Failed to add reboot suppression property on uninstall."); // Ignore all dependencies, since the Burn engine already performed the check. hr = StrAllocFormattedSecure(&sczProperties, L"%ls %ls=ALL", sczProperties, DEPENDENCY_IGNOREDEPENDENCIES); ExitOnFailure(hr, "Failed to add the list of dependencies to ignore to the properties."); hr = WiuRemovePatches(sczPatches, pExecuteAction->mspTarget.sczTargetProductCode, sczProperties, &restart); ExitOnFailure(hr, "Failed to uninstall MSP package."); break; } LExit: WiuUninitializeExternalUI(&context); ReleaseStr(sczCachedDirectory); ReleaseStr(sczMspPath); StrSecureZeroFreeString(sczProperties); ReleaseStr(sczObfuscatedProperties); ReleaseStr(sczPatches); switch (restart) { case WIU_RESTART_NONE: *pRestart = BOOTSTRAPPER_APPLY_RESTART_NONE; break; case WIU_RESTART_REQUIRED: *pRestart = BOOTSTRAPPER_APPLY_RESTART_REQUIRED; break; case WIU_RESTART_INITIATED: *pRestart = BOOTSTRAPPER_APPLY_RESTART_INITIATED; break; } // Best effort to clear the execute package cache folder and action variables. VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_CACHE_FOLDER, NULL, TRUE); VariableSetString(pVariables, BURN_BUNDLE_EXECUTE_PACKAGE_ACTION, NULL, TRUE); return hr; }
HRESULT ScaWriteWebError(IMSAdminBase* piMetabase, int iParentType, LPCWSTR wzRoot, SCA_WEB_ERROR* psweList) { // AssertSz(0, "Debug ScaWriteWebError here"); Assert(*wzRoot && psweList); HRESULT hr = S_OK; DWORD cchData = 0; LPWSTR pwzSearchKey = NULL; LPWSTR pwz = NULL; LPWSTR pwzErrors = NULL; LPWSTR pwzCodeSubCode = NULL; LPWSTR pwzAcceptableCodeSubCode = NULL; LPCWSTR wzFoundCodeSubCode = NULL; DWORD_PTR dwFoundCodeSubCodeIndex = 0xFFFFFFFF; BOOL fOldValueFound = FALSE; LPWSTR pwzAcceptableErrors = NULL; LPWSTR pwzNewError = NULL; METADATA_RECORD mr; ::ZeroMemory(&mr, sizeof(mr)); ExitOnNull(piMetabase, hr, E_INVALIDARG, "Failed to write web error, because no metabase was provided"); ExitOnNull(wzRoot, hr, E_INVALIDARG, "Failed to write web error, because no root was provided"); // get the set of all valid custom errors from the metabase mr.dwMDIdentifier = MD_CUSTOM_ERROR_DESC; mr.dwMDAttributes = METADATA_INHERIT; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.dwMDDataLen = cchData = 0; mr.pbMDData = NULL; hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, L"/LM/W3SVC/Info", &mr); ExitOnFailure(hr, "Unable to get set of acceptable error codes for this server."); pwzAcceptableErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); // Check if web errors already exist here mr.dwMDIdentifier = MD_CUSTOM_ERROR; mr.dwMDAttributes = METADATA_INHERIT; mr.dwMDUserType = IIS_MD_UT_SERVER; mr.dwMDDataType = ALL_METADATA; mr.dwMDDataLen = cchData = 0; mr.pbMDData = NULL; hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, wzRoot, &mr); if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) { // // If we don't have one already, find an appropriate one to start with // // we can walk up key by key and look for custom errors to inherit hr = StrAllocConcat(&pwzSearchKey, wzRoot, 0); ExitOnFailure1(hr, "Failed to copy root string: %ls", wzRoot); pwz = pwzSearchKey + lstrlenW(pwzSearchKey); while (NULL == pwzErrors) { // find the last slash while (*pwz != '/' && pwz != pwzSearchKey) pwz --; if (pwz == pwzSearchKey) break; *pwz = L'\0'; // Try here. If it's not found, keep walking up the path hr = MetaGetValue(piMetabase, METADATA_MASTER_ROOT_HANDLE, pwzSearchKey, &mr); if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr || MD_ERROR_DATA_NOT_FOUND == hr) hr = S_FALSE; ExitOnFailure1(hr, "failed to discover default error values to start with for web root: %ls while walking up the tree", wzRoot); if (S_OK == hr) { pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); break; } // Don't keep going if we're at the root if (0 == lstrcmpW(pwz + 1, L"W3SVC")) break; } } else { pwzErrors = reinterpret_cast<LPWSTR>(mr.pbMDData); } ExitOnFailure1(hr, "failed to discover default error values to start with for web root: %ls", wzRoot); // The above code should have come up with some value to start pwzErrors off with. Make sure it did. if (NULL == pwzErrors) { ExitOnFailure1(hr = E_UNEXPECTED, "failed to discover default error values to start with for web root: %ls", wzRoot); } // Loop through the web errors for (SCA_WEB_ERROR* pswe = psweList; pswe; pswe = pswe->psweNext) { // Assume that we will have to replace fOldValueFound = TRUE; // If the subcode is 0, that means "*" in MD_CUSTOM_ERROR (thus the special formatting logic) if (0 == pswe->iSubCode) { hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,*", pswe->iErrorCode); ExitOnFailure(hr, "failed to create error code string while installing web error"); } else { hr = StrAllocFormatted(&pwzCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); } hr = MultiSzFindSubstring(pwzErrors, pwzCodeSubCode, &dwFoundCodeSubCodeIndex, &wzFoundCodeSubCode); ExitOnFailure1(hr, "failed to find existing error code,subcode: %ls", pwzCodeSubCode); // If we didn't find this error code/sub code pair in the list already, make sure it's acceptable to add if (S_FALSE == hr) { // // Make sure this error code/sub code pair is in the "acceptable" list // // If the subcode is 0, that means "0" in MD_CUSTOM_ERROR_DESC (no special formatting logic needed) hr = StrAllocFormatted(&pwzAcceptableCodeSubCode, L"%d,%d", pswe->iErrorCode, pswe->iSubCode); ExitOnFailure(hr, "failed to create error code,subcode string while installing web error"); // We don't care where it is, just whether it's there or not hr = MultiSzFindSubstring(pwzAcceptableErrors, pwzAcceptableCodeSubCode, NULL, NULL); ExitOnFailure1(hr, "failed to find whether or not error code, subcode: %ls is supported", pwzCodeSubCode); if (S_FALSE == hr) { WcaLog(LOGMSG_VERBOSE, "Skipping error code, subcode: %ls because it is not supported by the server.", pwzCodeSubCode); continue; } // If we didn't find it (and its an acceptable error) then we have nothing to replace fOldValueFound = FALSE; } // Set up the new error string if needed if (*(pswe->wzFile)) { hr = StrAllocFormatted(&pwzNewError, L"%s,FILE,%s", pwzCodeSubCode, pswe->wzFile); ExitOnFailure2(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); } else if (*(pswe->wzURL)) { hr = StrAllocFormatted(&pwzNewError, L"%s,URL,%s", pwzCodeSubCode, pswe->wzURL); ExitOnFailure2(hr, "failed to create new error code string with code,subcode: %ls, file: %ls", pwzCodeSubCode, pswe->wzFile); } else if (fOldValueFound) { // If no File or URL was specified, they want a default error so remove the old value from the MULTISZ and move on hr = MultiSzRemoveString(&pwzErrors, dwFoundCodeSubCodeIndex); ExitOnFailure1(hr, "failed to remove string for error code sub code: %ls in order to make it 'default'", pwzCodeSubCode); continue; } // If we have something to replace, replace it, otherwise, put it at the beginning (order shouldn't matter) if (fOldValueFound) { hr = MultiSzReplaceString(&pwzErrors, dwFoundCodeSubCodeIndex, pwzNewError); ExitOnFailure1(hr, "failed to replace old error string with new error string for error code,subcode: %ls", pwzCodeSubCode); } else { hr = MultiSzPrepend(&pwzErrors, NULL, pwzNewError); ExitOnFailure1(hr, "failed to prepend new error string for error code,subcode: %ls", pwzCodeSubCode); } } // now write the CustomErrors to the metabase if (weptWeb == iParentType) { hr = ScaWriteMetabaseValue(piMetabase, wzRoot, L"/Root", MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); ExitOnFailure(hr, "Failed to write Web Error to /Root"); } else { hr = ScaWriteMetabaseValue(piMetabase, wzRoot, NULL, MD_CUSTOM_ERROR, METADATA_INHERIT, IIS_MD_UT_FILE, MULTISZ_METADATA, pwzErrors); ExitOnFailure(hr, "Failed to write Web Error"); } LExit: ReleaseStr(pwzErrors); ReleaseStr(pwzSearchKey); ReleaseStr(pwzCodeSubCode); ReleaseStr(pwzAcceptableCodeSubCode); ReleaseStr(pwzAcceptableErrors); return hr; }