//************************************************************* // // Copyright (c) Microsoft Corporation 1998 // All rights reserved // // Applist.cxx // //************************************************************* #include "appmgext.hxx" // // CAppList // CAppList::CAppList( CManagedAppProcessor * pManApp, CRsopAppContext * pRsopContext ) : _pManApp( pManApp ), _bRsopInitialized( FALSE ), _hrRsopInit( E_FAIL ), _pRsopContext( pRsopContext ) {} CAppList::~CAppList() { CAppInfo * pAppInfo; Reset(); while ( pAppInfo = (CAppInfo *) GetCurrentItem() ) { MoveNext(); pAppInfo->Remove(); delete pAppInfo; } ResetEnd(); } DWORD CAppList::SetAppActions() { CAppInfo * pAppInfo; DWORD Pass; DWORD Status; for ( Pass = 0; Pass <= 5; Pass++ ) { Reset(); for (;;) { pAppInfo = (CAppInfo *) GetCurrentItem(); if ( ! pAppInfo ) break; // // Apps get applied from lowest priority to highest priority. // Thus if one app fails to apply there is nothing of interest that // we can really do. We don't know which lower priority apps // may need to be "undone", and we shouldn't abort and thereby // prevent higher priority apps from being processed. // // Therefore, other than logging events, we ignore any errors in // the processing and continuing applying all apps. // switch ( Pass ) { case 0 : Status = pAppInfo->InitializePass0(); if ( Status != ERROR_SUCCESS ) return Status; break; case 1 : pAppInfo->SetActionPass1(); break; case 2 : pAppInfo->SetActionPass2(); break; case 3 : pAppInfo->SetActionPass3(); break; case 4 : pAppInfo->SetActionPass4(); break; case 5 : if ( pAppInfo->_Status != ERROR_SUCCESS ) return pAppInfo->_Status; break; } MoveNext(); } ResetEnd(); } return ERROR_SUCCESS; } DWORD CAppList::ProcessPolicy() { CAppInfo * pAppInfo; DWORD Pass; DWORD Status; DWORD FinalStatus; HRESULT hr; FinalStatus = ERROR_SUCCESS; Status = _pManApp->Impersonate(); if ( ERROR_SUCCESS == Status ) Status = SetAppActions(); if ( Status != ERROR_SUCCESS ) { // // Ensure that we log an event in this case so that RSoP // failed view at the extension level has enough information // to allow the administrator to diagnose the problem // gpEvents->PolicyAbort(); _pManApp->Revert(); _pManApp->GetRsopContext()->SetPolicyAborted( Status ); return Status; } if ( _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { return Status; } for ( Pass = 0; Pass <= 2; Pass++ ) { for ( Reset();;MoveNext() ) { pAppInfo = (CAppInfo *) GetCurrentItem(); if ( ! pAppInfo ) break; switch ( Pass ) { case 0 : Status = pAppInfo->ProcessUnapplyActions(); break; case 1 : Status = pAppInfo->ProcessApplyActions(); break; case 2 : if ( _pRsopContext->IsRsopEnabled() && _pRsopContext->IsDiagnosticModeEnabled() ) Status = pAppInfo->ProcessTransformConflicts(); break; } if ( (FinalStatus == ERROR_SUCCESS) && (Status != ERROR_SUCCESS) ) FinalStatus = Status; // // If we are returning an error of some sort, we should force a synchronous // refresh if we are not already returning the error to request one. This is // needed because some errors require a sync refresh to fix (such as // install / uninstall errors), and gp will not give us a sync refresh // unless we ask for it. // if ( ! _pManApp->NoChanges() && ( ERROR_SUCCESS != FinalStatus ) && ( ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED != FinalStatus ) ) { _pManApp->Revert(); (void) ForceSynchronousRefresh( _pManApp->UserToken() ); _pManApp->Impersonate(); } } ResetEnd(); } _pManApp->Revert(); return FinalStatus; } DWORD CAppList::ProcessARPList() { DWORD Status; Status = _pManApp->Impersonate(); if ( ERROR_SUCCESS == Status ) Status = SetAppActions(); _pManApp->Revert(); return Status; } DWORD CAppList::Count( DWORD Flags ) { CAppInfo * pAppInfo; DWORD Count; Count = 0; Reset(); for ( ;; ) { pAppInfo = (CAppInfo *) GetCurrentItem(); if ( ! pAppInfo ) break; if ( pAppInfo->_ActFlags & Flags ) Count++; MoveNext(); } ResetEnd(); return Count; } CAppInfo * CAppList::Find( GUID DeploymentId ) { CAppInfo * pAppInfo; Reset(); for (;;) { pAppInfo = (CAppInfo *) GetCurrentItem(); if ( ! pAppInfo ) break; if ( memcmp( &pAppInfo->_DeploymentId, &DeploymentId, sizeof(GUID) ) == 0 ) break; MoveNext(); } ResetEnd(); return pAppInfo; } HRESULT CAppList::WriteLog( DWORD dwFilter ) { CAppInfo * pAppInfo; DWORD Status; HRESULT hr; hr = InitRsopLog(); if ( FAILED(hr) ) { return hr; } if ( ( CRsopAppContext::POLICY_REFRESH == _pRsopContext->GetContext() ) && _pRsopContext->IsDiagnosticModeEnabled() && ! _pManApp->IsRemovingPolicies() ) { hr = PurgeEntries(); } if ( FAILED(hr) ) { return hr; } Reset(); for (;;) { BOOL bLogApp; pAppInfo = (CAppInfo *) GetCurrentItem(); if ( ! pAppInfo ) break; // // Check to see if this app is applied to the user and should be logged // bLogApp = ( RSOP_FILTER_ALL == dwFilter ) || ( ( ACTION_UNINSTALL == pAppInfo->Action() ) || ( ACTION_ORPHAN == pAppInfo->Action() ) ); // // Check to see if this app would have been in the ARP list if not for the // fact that the administrator chose to conceal it // if ( CRsopAppContext::ARPLIST == _pRsopContext->GetContext() ) { if ( ( ACTION_NONE == pAppInfo->Action() ) && ( (pAppInfo->_ActFlags & (ACTFLG_Assigned | ACTFLG_Published) ) && !(pAppInfo->_ActFlags & ACTFLG_UserInstall) ) ) { bLogApp = TRUE; pAppInfo->SetAction( ACTION_INSTALL, 0, NULL); } } if ( bLogApp ) { hr = WriteAppToRsopLog( pAppInfo ); if (FAILED(hr)) { break; } } MoveNext(); } ResetEnd(); return hr; } HRESULT CAppList::WriteAppToRsopLog( CAppInfo* pAppInfo ) { HRESULT hr; CConflict WinningConflict( pAppInfo ); // // If this is a rolled-back upgrade, the instance // is already written and we do not need to do anything // if ( pAppInfo->_bRollback ) { return S_OK; } // // Do not log entries for applications that will be removed // in the next sync refresh // if ( ( ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED == pAppInfo->_Status ) && ( ACTION_APPLY != pAppInfo->_Action ) && ( ACTION_INSTALL != pAppInfo->_Action ) && ( ACTION_REINSTALL != pAppInfo->_Action ) ) { return S_OK; } hr = InitRsopLog(); if ( FAILED(hr) ) { return hr; } // // If this app failed to be applied, we must log it anyway // if ( pAppInfo->_StatusList.GetCurrentItem() ) { if ( ACTION_NONE == pAppInfo->Action() ) { pAppInfo->SetAction( ACTION_APPLY, pAppInfo->_dwApplyCause, NULL); } } else if ( _pRsopContext->HasPolicyAborted() ) { // // If policy aborted before even trying to apply the app // then we shouldn't log this setting unless it has a failure -- // apps without failures haven't been applied, and apps with // failures are known to not apply, so we can safely log those // return S_OK; } switch ( pAppInfo->GetRsopEntryType() ) { case APP_ATTRIBUTE_ENTRYTYPE_VALUE_INSTALLED_PACKAGE: WCHAR* wszCriteria; wszCriteria = pAppInfo->GetRsopAppCriteria(); hr = ClearLog( wszCriteria, TRUE ); delete [] wszCriteria; if ( FAILED(hr) ) { return hr; } // // In planning mode, we only apply published apps if they upgrade // an assigned app // if ( _pRsopContext->IsPlanningModeEnabled() ) { if ( ( ACTFLG_Published & pAppInfo->_ActFlags ) && ! pAppInfo->_bSupersedesAssigned ) { break; } } // // Fall through to the next case // case APP_ATTRIBUTE_ENTRYTYPE_VALUE_ARPLIST_ITEM: // // In the arplist case, we require the action // set to ACTION_INSTALL in order to log the app // if ( _pManApp->ARPList() && ( ACTION_INSTALL != pAppInfo->Action() ) ) { break; } // // If this is a winning application, create a new entry for the winner // and then log its conflicts // if ( ! pAppInfo->IsSuperseded() ) { WCHAR wszDeploymentId[ MAX_SZGUID_LEN ]; pAppInfo->GetDeploymentId( wszDeploymentId); hr = WinningConflict.SetConflictId( wszDeploymentId ); if ( SUCCEEDED(hr) ) { hr = WriteNewRecord ( &WinningConflict ); } if (FAILED(hr)) { DebugMsg((DM_VERBOSE, IDS_RSOP_LOG_WRITE_FAIL, hr)); hr = S_OK; } if (SUCCEEDED(hr)) { (void) WinningConflict.LogFailure(); } (void) WriteConflicts ( pAppInfo ); } break; case APP_ATTRIBUTE_ENTRYTYPE_VALUE_REMOVED_PACKAGE: BOOL bDeleteInstalledEntry; CAppStatus* pCurrentStatus; bDeleteInstalledEntry = TRUE; pCurrentStatus = (CAppStatus*) pAppInfo->_StatusList.GetCurrentItem(); // // Do not delete the installed entry if it was never // successfully removed // if ( pCurrentStatus && ( RSOPFailed == pCurrentStatus->_SettingStatus ) ) { bDeleteInstalledEntry = FALSE; } // // For removed applications, we do not create a new entry, // just change the existing entry to indicate that // it has been removed // hr = MarkRSOPEntryAsRemoved( pAppInfo, bDeleteInstalledEntry); if ( SUCCEEDED( hr ) ) { pAppInfo->_bRemovalLogged = TRUE; } break; default: break; } return hr; } HRESULT CAppList::WriteConflicts( CAppInfo* pAppInfo ) { HRESULT hr; CConflictList Conflicts; CConflict* pCurrentConflict; hr = pAppInfo->GetConflictTable()->GenerateResultantConflictList( &Conflicts ); if (SUCCEEDED(hr)) { Conflicts.Reset(); while ( pCurrentConflict = (CConflict*) Conflicts.GetCurrentItem() ) { WCHAR wszDeploymentId[ MAX_SZGUID_LEN ]; pAppInfo->GetDeploymentId( wszDeploymentId); hr = pCurrentConflict->SetConflictId( wszDeploymentId ); if ( FAILED(hr) ) { break; } HRESULT hrWrite; if ( ! pCurrentConflict->GetApp()->IsLocal() ) { hrWrite = WriteNewRecord( pCurrentConflict ); } else { hrWrite = OpenExistingRecord( pCurrentConflict ); if ( SUCCEEDED( hrWrite ) ) { hrWrite = pCurrentConflict->Write(); } if ( SUCCEEDED( hrWrite ) ) { hrWrite = pCurrentConflict->GetApp()->ClearRemovalProperties( pCurrentConflict ); } if ( SUCCEEDED( hrWrite ) ) { hrWrite = CommitRecord( pCurrentConflict ); } } if ( SUCCEEDED( hrWrite ) ) { (void) DeleteStatusRecords( pCurrentConflict ); } if (FAILED(hrWrite)) { DebugMsg((DM_VERBOSE, IDS_RSOP_LOG_WRITE_FAIL, hrWrite)); } Conflicts.MoveNext(); } } if (FAILED(hr)) { DebugMsg((DM_VERBOSE, IDS_RSOP_CONFLICTS_FAIL, pAppInfo->_pwszDeploymentName, pAppInfo->_pwszGPOName, hr)); } return hr; } HRESULT CAppList::InitRsopLog() { if ( _bRsopInitialized ) { return _hrRsopInit; } _bRsopInitialized = TRUE; _hrRsopInit = InitLog( _pRsopContext, RSOP_MANAGED_SOFTWARE_APPLICATION); if (FAILED(_hrRsopInit)) { DebugMsg((DM_VERBOSE, IDS_RSOP_LOG_INIT_FAIL, _hrRsopInit)); return _hrRsopInit; } if ( _pRsopContext->Transition() ) { _hrRsopInit = ClearLog( NULL, TRUE ); } else if ( ! _pRsopContext->ForcedRefresh() ) { BOOL bPolicy; bPolicy = FALSE; // // In the forced refresh case, we need to preserve the // state of policy since it actually has not changed, // so we skip the purge below // // // In the policy refresh case, we need to clear everything // that does not apply to the user as well as removal entries // switch ( _pRsopContext->GetContext() ) { case CRsopAppContext::POLICY_REFRESH: bPolicy = TRUE; if ( ! _pRsopContext->PurgeRemovalEntries() ) { break; } _pRsopContext->ResetRemovalPurge(); // // Purposefully fall through // case CRsopAppContext::ARPLIST: _hrRsopInit = ClearLog( GetRsopListCriteria(), bPolicy ); if ( FAILED( _hrRsopInit ) ) { DebugMsg((DM_VERBOSE, IDS_RSOP_LOG_INIT_FAIL, _hrRsopInit)); } break; default: break; } } return _hrRsopInit; } HRESULT CAppList::PurgeEntries() { HRESULT hr; hr = GetEnum( RSOP_PURGE_QUERY ); if ( SUCCEEDED(hr) ) { HRESULT hrEnum; hrEnum = S_OK; for (;;) { CPolicyRecord CurrentApplication; LONG EntryType; hrEnum = GetNextRecord( &CurrentApplication ); if ( S_OK != hrEnum ) { if ( FAILED(hrEnum) ) { hr = hrEnum; } break; } GUID DeploymentId; WCHAR wszDeploymentId[ MAX_SZGUID_LEN ]; LONG cchSize; CAppInfo* pAppliedApp; cchSize = sizeof( wszDeploymentId ) / sizeof( *wszDeploymentId ); hrEnum = CurrentApplication.GetValue( RSOP_ATTRIBUTE_ID, wszDeploymentId, &cchSize); if ( FAILED( hrEnum ) ) { break; } if ( S_OK != hrEnum ) { break; } StringToGuid( wszDeploymentId, &DeploymentId ); pAppliedApp = Find( DeploymentId ); if ( pAppliedApp ) { if ( pAppliedApp->_State & ( APPSTATE_ASSIGNED | APPSTATE_PUBLISHED ) ) { if ( ACTFLG_Published & pAppliedApp->_ActFlags ) { (void) GetUserApplyCause( &CurrentApplication, pAppliedApp); } continue; } if ( ( ACTION_NONE != pAppliedApp->Action() ) || pAppliedApp->_bRollback ) { continue; } } hrEnum = DeleteRecord( &CurrentApplication, TRUE ); if ( FAILED( hrEnum ) ) { hr = hrEnum; } } FreeEnum(); } return hr; } HRESULT CAppList::GetUserApplyCause( CPolicyRecord* pRecord, CAppInfo* pAppInfo ) { HRESULT hr; LONG ApplyCause; hr = pRecord->GetValue( APP_ATTRIBUTE_APPLY_CAUSE, &ApplyCause); if ( SUCCEEDED( hr) && ( APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE != ApplyCause ) && ! pAppInfo->_wszDemandProp ) { pAppInfo->_dwUserApplyCause = ApplyCause; switch ( ApplyCause ) { case APP_ATTRIBUTE_APPLYCAUSE_VALUE_FILEEXT: pAppInfo->_wszDemandProp = APP_ATTRIBUTE_ONDEMAND_FILEEXT; break; case APP_ATTRIBUTE_APPLYCAUSE_VALUE_CLSID: pAppInfo->_wszDemandProp = APP_ATTRIBUTE_ONDEMAND_CLSID; break; case APP_ATTRIBUTE_APPLYCAUSE_VALUE_PROGID: pAppInfo->_wszDemandProp = APP_ATTRIBUTE_ONDEMAND_PROGID; break; default: pAppInfo->_wszDemandProp = NULL; break; } if ( pAppInfo->_wszDemandProp && ! pAppInfo->_wszDemandSpec ) { LONG cchSize; cchSize = 0; hr = pRecord->GetValue( pAppInfo->_wszDemandProp, pAppInfo->_wszDemandSpec, &cchSize); if ( S_FALSE == hr ) { pAppInfo->_wszDemandSpec = new WCHAR [ cchSize ]; if ( pAppInfo->_wszDemandSpec ) { hr = pRecord->GetValue( pAppInfo->_wszDemandProp, pAppInfo->_wszDemandSpec, &cchSize); } else { hr = E_OUTOFMEMORY; } } } } return hr; } HRESULT CAppList::MarkRSOPEntryAsRemoved( CAppInfo* pAppInfo, BOOL bRemoveInstances) { HRESULT hr; WCHAR* wszRemovalCriteria; wszRemovalCriteria = NULL; DebugMsg((DM_VERBOSE, IDS_RSOP_LOG_WRITE_INFO, pAppInfo->_pwszDeploymentName, pAppInfo->_pwszGPOName)); // // If this is a removed app that was reapplied as part of upgrade rollback, // we do not want to remove the instances // if ( pAppInfo->_bRollback ) { bRemoveInstances = FALSE; } // // Set up this list's rsop enumerator to enumerate instances of this application // hr = FindRsopAppEntry( pAppInfo, &wszRemovalCriteria ); if ( SUCCEEDED( hr ) ) { for (;;) { CConflict RemovedApplication( pAppInfo ); LONG Precedence; hr = GetNextRecord( &RemovedApplication ); if ( S_OK != hr ) { break; } hr = RemovedApplication.GetValue( RSOP_ATTRIBUTE_PRECEDENCE, &Precedence); if ( FAILED (hr) ) { break; } // // If this is a removal of an assigned application, then we should // change the apply cause of the installed application to assigned // rather than user // if ( 1 == Precedence ) { if ( ( pAppInfo->_State & APPSTATE_ASSIGNED ) && ( CRsopAppContext::REMOVAL == _pRsopContext->GetContext() ) ) { LONG CurrentApplyCause; LONG CurrentEligibility; // // First, we must find out the current apply cause so // that we can propagate that to the removal entry // hr = RemovedApplication.GetValue( APP_ATTRIBUTE_APPLY_CAUSE, &CurrentApplyCause); if ( SUCCEEDED(hr) ) { hr = RemovedApplication.GetValue( APP_ATTRIBUTE_ELIGIBILITY, &CurrentEligibility); } if ( SUCCEEDED(hr) ) { // // Now set the current apply cause to assigned // hr = RemovedApplication.SetValue( APP_ATTRIBUTE_APPLY_CAUSE, APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_APPLY_CAUSE, hr ); if ( SUCCEEDED(hr) ) { // // Also set the eligibility to assigned // hr = RemovedApplication.SetValue( APP_ATTRIBUTE_ELIGIBILITY, APP_ATTRIBUTE_ELIGIBILITY_VALUE_ASSIGNED); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ELIGIBILITY, hr ); } // // Clear out any attributes that should not be set for applications // applied due to assignment // hr = RemovedApplication.ClearValue( APP_ATTRIBUTE_ONDEMAND_FILEEXT); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND_FILEEXT, hr ) hr = RemovedApplication.ClearValue( APP_ATTRIBUTE_ONDEMAND_CLSID); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND_CLSID, hr ) hr = RemovedApplication.ClearValue( APP_ATTRIBUTE_ONDEMAND_PROGID); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND_PROGID, hr ) } // // Commit the record for the installed application // if ( SUCCEEDED(hr) ) { hr = CommitRecord( &RemovedApplication ); } if ( SUCCEEDED(hr) ) { // // Now set the removal entry's install cause to // that of the original install // hr = RemovedApplication.SetValue( APP_ATTRIBUTE_APPLY_CAUSE, CurrentApplyCause); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_APPLY_CAUSE, hr ); if ( SUCCEEDED(hr) ) { // // Also set the eligibility to assigned // hr = RemovedApplication.SetValue( APP_ATTRIBUTE_ELIGIBILITY, CurrentEligibility); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ELIGIBILITY, hr ); } } } // // We must mark the highest precedence entry // (the currently applied entry) as removed -- // see if this has the highest precedence (1) // if ( bRemoveInstances && ( ( pAppInfo->_State & APPSTATE_PUBLISHED ) || ( CRsopAppContext::POLICY_REFRESH != _pRsopContext->GetContext() ) ) ) { hr = DeleteRecord ( &RemovedApplication, TRUE ); } else { hr = DeleteStatusRecords( &RemovedApplication ); } if ( SUCCEEDED( hr ) ) { hr = RemovedApplication.SetValue( APP_ATTRIBUTE_ENTRYTYPE, APP_ATTRIBUTE_ENTRYTYPE_VALUE_REMOVED_PACKAGE); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ENTRYTYPE, hr ); } if ( SUCCEEDED( hr ) ) { hr = RemovedApplication.SetValue( RSOP_ATTRIBUTE_PRECEDENCE, 0L); REPORT_ATTRIBUTE_SET_STATUS( RSOP_ATTRIBUTE_PRECEDENCE, hr ); } if ( SUCCEEDED( hr ) ) { hr = pAppInfo->WriteRemovalProperties( &RemovedApplication ); } if ( SUCCEEDED( hr ) ) { hr = CommitRecord( &RemovedApplication ); if ( SUCCEEDED(hr) ) { (void) DeleteStatusRecords( &RemovedApplication ); (void) RemovedApplication.LogFailure(); } } } else { hr = DeleteRecord ( &RemovedApplication, TRUE ); } if ( FAILED(hr) ) { break; } } } FreeEnum(); if ( bRemoveInstances && SUCCEEDED(hr) ) { // // We've already found the highest precedence entry // and copied it as a removal entry -- // if the caller specified to remove the original // instances for this app's conflict id, do so // hr = ClearLog( wszRemovalCriteria, TRUE ); } delete [] wszRemovalCriteria; return hr; } HRESULT CAppList::FindRsopAppEntry( CAppInfo* pAppInfo, WCHAR** ppwszAppCriteria ) { HRESULT hr; hr = InitRsopLog(); if ( FAILED(hr) ) { return hr; } hr = E_OUTOFMEMORY; *ppwszAppCriteria = pAppInfo->GetRsopAppCriteria(); if ( *ppwszAppCriteria ) { hr = GetEnum( *ppwszAppCriteria ); } if ( FAILED(hr) ) { delete [] *ppwszAppCriteria; *ppwszAppCriteria = NULL; } return hr; } WCHAR* CAppList::GetRsopListCriteria() { WCHAR* wszCriteria; switch ( _pRsopContext->GetContext() ) { case CRsopAppContext::ARPLIST: wszCriteria = RSOP_ARP_CONTEXT_QUERY; break; case CRsopAppContext::POLICY_REFRESH: wszCriteria = RSOP_POLICY_CONTEXT_QUERY; break; default: ASSERT(FALSE); return NULL; } return wszCriteria; }