|
|
//*************************************************************
//
// Copyright (c) Microsoft Corporation 1998
// All rights reserved
//
// Appinfo.cxx
//
//*************************************************************
#include "appmgext.hxx"
//
// CAppInfo
//
// Initialization from the Directory.
CAppInfo::CAppInfo( CManagedAppProcessor * pManApp, PACKAGEDISPINFO * pPackageInfo, BOOL bDemandInstall, BOOL & bStatus ) { _pManApp = pManApp; _DemandInstall = bDemandInstall;
_DeploymentId = pPackageInfo->PackageGuid;
bStatus = Initialize( pPackageInfo );
if ( ! bStatus ) DebugMsg((DM_WARNING, IDS_APPINFO_FAIL, pPackageInfo->pszPackageName)); }
// Initialization from the registry.
CAppInfo::CAppInfo( CManagedAppProcessor * pManApp, WCHAR * pwszDeploymentId, BOOL & bStatus ) { _pManApp = pManApp; _DemandInstall = FALSE;
StringToGuid( pwszDeploymentId, &_DeploymentId );
_StatusList.Reset();
bStatus = Initialize( NULL );
if ( ! bStatus ) DebugMsg((DM_WARNING, IDS_LOCALAPPINFO_FAIL, pwszDeploymentId)); }
// Initialization from a local script file.
CAppInfo::CAppInfo( WCHAR * pwszDeploymentId ) { _pManApp = 0; StringToGuid( pwszDeploymentId, &_DeploymentId ); (void) Initialize( NULL ); }
BOOL CAppInfo::Initialize( PACKAGEDISPINFO * pPackageInfo ) { HKEY hkApp; WCHAR wszDeploymentId[44]; DWORD Length; DWORD Size; DWORD n, i; DWORD Status; WCHAR* wszSomId; HRESULT hr;
_pwszDeploymentName = 0; _pwszGPOId = 0; _pwszGPOName = 0; _pwszSOMId = 0; _pwszGPODSPath = 0; _pwszProductId = 0; _pwszLocalScriptPath = 0; _pwszGPTScriptPath = 0; _Upgrades = 0; _pUpgrades = 0; _Overrides = 0; _pOverrides = 0; _pwszSupercededIds = 0; _pwszPublisher = 0; _pwszSupportURL = 0; _VersionHi = 0; _VersionLo = 0; _PathType = DrwFilePath; memset( &_USN, 0, sizeof(_USN) ); _LangId = LANG_NEUTRAL; _LanguageWeight = 0; _AssignCount = 0; _LocalRevision = 0; memset( &_ScriptTime, 0, sizeof(_ScriptTime) ); _DirectoryRevision = 0; _InstallUILevel = INSTALLUILEVEL_DEFAULT; _ActFlags = 0; _InstallState = INSTALLSTATE_UNKNOWN; _State = 0; _Action = ACTION_NONE; _Status = ERROR_SUCCESS; _bNeedsUnmanagedRemove = FALSE; _rgSecurityDescriptor = 0; _cbSecurityDescriptor = 0; _bSuperseded = FALSE; _bRollback = FALSE; _bRemovalLogged = FALSE; _bTransformConflict = FALSE; _bRestored = FALSE; _rgwszTransforms = NULL; _rgwszCategories = NULL; _cTransforms = 0; _cCategories = 0; _pwszPackageLocation = 0; _pwszRemovingDeploymentId = 0; _cArchitectures = 0; _rgArchitectures = NULL; _PrimaryArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN; _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE; _dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_NONE; _bSupersedesAssigned = FALSE; _dwUserApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE; _wszDemandSpec = NULL; _wszDemandProp = NULL;
if ( ! _pManApp ) return FALSE;
Status = ERROR_SUCCESS;
Length = lstrlen( _pManApp->LocalScriptDir() );
_pwszLocalScriptPath = new WCHAR[Length + 44]; if ( ! _pwszLocalScriptPath ) return FALSE;
hr = StringCchCopy( _pwszLocalScriptPath, Length+44, _pManApp->LocalScriptDir() ); if (FAILED(hr)) { delete [] _pwszLocalScriptPath; return FALSE; } GuidToString( _DeploymentId, &_pwszLocalScriptPath[Length] ); hr = StringCchCopy( &_pwszLocalScriptPath[Length+GUIDSTRLEN], 44-GUIDSTRLEN,L".aas" ); if (FAILED(hr)) { delete [] _pwszLocalScriptPath; return FALSE; }
if ( CRsopAppContext::REMOVAL == _pManApp->GetRsopContext()->GetContext() ) { _dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_USER; } else if ( _pManApp->GetRsopContext()->RemoveGPOApps() ) { _dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_SCOPELOSS; }
if ( pPackageInfo ) { CGPOInfo* pGpoInfo; CGPOInfoList& GpoInfoList = _pManApp->GPOList();
_VersionHi = pPackageInfo->dwVersionHi; _VersionLo = pPackageInfo->dwVersionLo;
_LangId = pPackageInfo->LangId; _LanguageWeight = GetLanguagePriority(LANGIDFROMLCID(pPackageInfo->LangId),pPackageInfo->dwActFlags);
_pwszGPTScriptPath = StringDuplicate( pPackageInfo->pszScriptPath ); if ( ! _pwszGPTScriptPath && !(_pManApp->ARPList())) return FALSE;
_pwszDeploymentName = StringDuplicate( pPackageInfo->pszPackageName );
GuidToString( pPackageInfo->ProductCode, &_pwszProductId ); GuidToString( pPackageInfo->GpoId, &_pwszGPOId );
if ( _pwszGPOId ) { pGpoInfo = GpoInfoList.Find( _pwszGPOId );
_pwszGPOName = StringDuplicate( pGpoInfo ? pGpoInfo->GetGPOName() : L"" ); }
_pwszPublisher = StringDuplicate( pPackageInfo->pszPublisher ); _pwszSupportURL = StringDuplicate( pPackageInfo->pszUrl );
if ( _pManApp->GetRsopContext()->IsRsopEnabled() || _pManApp->ARPList() ) Status = InitializeCategoriesList( pPackageInfo );
if ( (ERROR_SUCCESS == Status ) && _pManApp->GetRsopContext()->IsRsopEnabled() ) { //
// We perform RSoP specific initialization here. Note that if
// any of these fails, we disable RSoP, but continue policy
// application. Any partial initialization due to an
// error will be cleaned up by the destructor
//
_pwszSOMId = StringDuplicate( pGpoInfo ? pGpoInfo->GetSOMPath() : L"" );
//
// Make copies of RSoP specific simple string data
//
if ( pPackageInfo->cbSecurityDescriptor ) { _rgSecurityDescriptor = new BYTE[ pPackageInfo->cbSecurityDescriptor ];
if ( _rgSecurityDescriptor && pPackageInfo->rgSecurityDescriptor ) { _cbSecurityDescriptor = pPackageInfo->cbSecurityDescriptor; memcpy( _rgSecurityDescriptor, pPackageInfo->rgSecurityDescriptor, pPackageInfo->cbSecurityDescriptor ); } }
_pwszGPODSPath = StringDuplicate( pPackageInfo->pszGpoPath );
//
// Check for memory allocation failures
//
if ( ! _pwszSOMId || ! _pwszGPODSPath ) { Status = ERROR_OUTOFMEMORY; }
//
// Now make copies of the more complex RSoP information
//
if ( ERROR_SUCCESS == Status ) Status = InitializeRSOPTransformsList( pPackageInfo );
if ( ERROR_SUCCESS == Status ) Status = InitializeRSOPArchitectureInfo( pPackageInfo );
if ( ERROR_SUCCESS != Status ) { hr = HRESULT_FROM_WIN32( Status );
_pManApp->GetRsopContext()->DisableRsop( hr ); } }
for ( n = 0; n < pPackageInfo->cUpgrades; n++ ) { if ( pPackageInfo->prgUpgradeInfoList[n].Flag & (UPGFLG_Uninstall | UPGFLG_NoUninstall) ) _Upgrades++; }
if ( _Upgrades > 0 ) { _pUpgrades = new UPGRADE_INFO[_Upgrades];
if ( ! _pUpgrades ) return FALSE;
memset( _pUpgrades, 0, sizeof(UPGRADE_INFO) * _Upgrades );
for ( n = 0, i = 0; n < pPackageInfo->cUpgrades; n++ ) { if ( ! (pPackageInfo->prgUpgradeInfoList[n].Flag & (UPGFLG_Uninstall | UPGFLG_NoUninstall)) ) continue;
_pUpgrades[i].DeploymentId = pPackageInfo->prgUpgradeInfoList[n].PackageGuid; _pUpgrades[i].Flags = UPGRADE_OVER; if ( pPackageInfo->prgUpgradeInfoList[n].Flag & UPGFLG_Uninstall ) _pUpgrades[i].Flags |= UPGRADE_UNINSTALL; else _pUpgrades[i].Flags |= UPGRADE_NOUNINSTALL; i++; } }
memcpy( &_USN, &pPackageInfo->Usn, sizeof(_USN) ); _DirectoryRevision = pPackageInfo->dwRevision; _InstallUILevel = pPackageInfo->InstallUiLevel; _PathType = pPackageInfo->PathType; _ActFlags = pPackageInfo->dwActFlags; }
GuidToString( _DeploymentId, wszDeploymentId);
if ( ERROR_SUCCESS == Status ) { if ( ! _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { //
// Need to request set value access so that we may
// delete the RemovedGPOState value if it exists -- because of this
// we must revert since the user may not have write access
//
(void) _pManApp->Revert();
Status = RegOpenKeyEx( _pManApp->AppmgmtKey(), wszDeploymentId, 0, KEY_READ | KEY_SET_VALUE, &hkApp );
DWORD impStatus = _pManApp->Impersonate(); if ( impStatus != ERROR_SUCCESS ) { Status = impStatus; } } else { Status = ERROR_FILE_NOT_FOUND; } }
if ( ERROR_SUCCESS == Status ) { Size = sizeof(DWORD);
_State = APPSTATE_PUBLISHED | APPSTATE_POLICYREMOVE_ORPHAN;
(void) RegQueryValueEx( hkApp, APPSTATEVALUE, 0, NULL, (LPBYTE) &_State, &Size );
Size = sizeof(DWORD);
//
// This is used to track the best case time when we could completely
// delete an appmgmt key after an app is unassigned.
//
(void) RegQueryValueEx( hkApp, ASSIGNCOUNTVALUE, 0, NULL, (LPBYTE) &_AssignCount, &Size );
Size = sizeof(DWORD);
//
// Beta2 systems didn't write this value. If it is not found we
// just use the default value of 0.
//
(void) RegQueryValueEx( hkApp, REVISIONVALUE, 0, NULL, (LPBYTE) &_LocalRevision, &Size );
if ( _LocalRevision > 0 ) { Size = sizeof(_ScriptTime);
(void) RegQueryValueEx( hkApp, SCRIPTTIMEVALUE, 0, NULL, (LPBYTE) &_ScriptTime, &Size ); }
if ( ! pPackageInfo ) { //
// This is needed so that if we are in a RemoveApp call in the service
// we will get the proper UI level to write back for an assigned app.
//
Size = sizeof(DWORD); (void) RegQueryValueEx( hkApp, INSTALLUI, 0, NULL, (LPBYTE) &_InstallUILevel, &Size );
ReadStringValue( hkApp, DEPLOYMENTNAMEVALUE, &_pwszDeploymentName ); ReadStringValue( hkApp, GPONAMEVALUE, &_pwszGPOName ); ReadStringValue( hkApp, GPOIDVALUE, &_pwszGPOId ); ReadStringValue( hkApp, PRODUCTIDVALUE, &_pwszProductId ); }
//
// During policy refresh, we need to ensure that apps that went out of
// scope on one machine don't come back on another
//
if ( (ERROR_SUCCESS == Status ) && _pManApp->RegularPolicyRun() ) { DWORD dwRemovedState; LONG StatusRemovedState;
Size = sizeof(DWORD);
//
// Check for an appstate saved if the app went out of scope
//
StatusRemovedState = RegQueryValueEx( hkApp, REMOVEDGPOSTATE, 0, NULL, (LPBYTE) &dwRemovedState, &Size );
if ( ERROR_SUCCESS == StatusRemovedState ) { BOOL bDeleteRemovedState;
bDeleteRemovedState = FALSE;
//
// We only restore the old app state if this app is currently
// set to be uninstalled or orphaned -- if not, this removed state is invalid
// so we will delete it
//
if ( ! ( ( APPSTATE_UNINSTALLED & _State ) || ( APPSTATE_ORPHANED & _State ) ) ) { bDeleteRemovedState = TRUE; } else if ( pPackageInfo || IsGpoInScope() ) { //
// The gpo for this app is back in scope, we will restore the state
// to the previous state before it went out of scope on the other machine
//
_State = dwRemovedState;
bDeleteRemovedState = TRUE;
Size = sizeof(DWORD);
//
// Set the state value back to the original state
//
(void) RegSetValueEx( hkApp, APPSTATEVALUE, 0, REG_DWORD, (LPBYTE) &dwRemovedState, sizeof(DWORD) );
_bRestored = TRUE;
DebugMsg((DM_VERBOSE, IDS_ABORT_SCOPELOSS, _pwszDeploymentName ? _pwszDeploymentName : L"" , _pwszGPOName ? _pwszGPOName : L"", _State )); } if ( bDeleteRemovedState ) { (void) RegDeleteValue( hkApp, REMOVEDGPOSTATE ); } } }
//
// If the app is currently assigned, treat that as the reason for it being applied.
// This may be overridden later if this app upgrades another
//
if ( ! ( APPSTATE_UNINSTALLED & _State ) && ( APPSTATE_ASSIGNED & _State ) ) { _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED; }
ReadStringValue( hkApp, SUPERCEDEDIDS, &_pwszSupercededIds );
RegCloseKey( hkApp ); }
CheckScriptExistence();
if ( ! _pwszDeploymentName || ! _pwszGPOId || ! _pwszGPOName || ! _pwszProductId ) return FALSE;
return TRUE; }
CAppInfo::~CAppInfo() {
//
// There are cases, like handling upgrades, where we copy the script
// early on to ensure that we can access it. Later however, the same
// app may be reset to do nothing or may fail to apply. This check here
// deletes any script we copied which we don't need now.
//
if ( ((_Status != ERROR_SUCCESS) || (ACTION_NONE == _Action)) && ! (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) && ((_State & APPSTATE_SCRIPT_NOT_EXISTED) && (_State & APPSTATE_SCRIPT_PRESENT)) ) { if ( _pwszLocalScriptPath ) DeleteFile( _pwszLocalScriptPath ); }
DWORD iCategory;
for ( iCategory = 0; iCategory < _cCategories; iCategory++ ) { delete [] _rgwszCategories[ iCategory ]; }
DWORD iTransform;
for ( iTransform = 0; iTransform < _cTransforms; iTransform++ ) { delete [] _rgwszTransforms[ iTransform ]; }
CAppStatus* pAppStatus;
//
// Clean up failure statuses
//
for ( _StatusList.Reset(); pAppStatus = (CAppStatus*) _StatusList.GetCurrentItem(); ) { _StatusList.MoveNext(); delete pAppStatus; }
delete [] _pwszDeploymentName; delete [] _pwszGPOName; delete [] _pwszGPOId; delete [] _pwszSOMId; delete [] _pwszGPODSPath; delete [] _pwszProductId; delete [] _pwszLocalScriptPath; delete [] _pwszGPTScriptPath; delete [] _pUpgrades; delete [] _pOverrides; delete [] _pwszSupercededIds; delete [] _pwszPublisher; delete [] _pwszSupportURL; delete [] _rgSecurityDescriptor; delete [] _rgwszCategories; delete [] _rgwszTransforms; delete [] _pwszPackageLocation; delete [] _pwszRemovingDeploymentId; delete [] _rgArchitectures; delete [] _wszDemandSpec; }
DWORD CAppInfo::InitializePass0() { UPGRADE_INFO * pUpgradeInfo;
if ( ! (_ActFlags & (ACTFLG_Assigned | ACTFLG_Published)) && ! (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) ) return ERROR_SUCCESS;
if ( ! _pManApp->ARPList() && (_State & APPSTATE_SCRIPT_EXISTED) ) _InstallState = (*gpfnMsiQueryProductState)( _pwszProductId );
for ( DWORD n = 0; n < _Upgrades; n++ ) { CAppInfo * pBaseApp;
if ( ! (_pUpgrades[n].Flags & UPGRADE_OVER) ) continue;
//
// Note that an apps' override list will include apps it really will not
// upgrade because of policy precedence violation. However, we keep these
// in the list for our detection of upgrade relationships when doing
// demand installs.
//
AddToOverrideList( &_pUpgrades[n].DeploymentId );
pBaseApp = _pManApp->AppList().Find( _pUpgrades[n].DeploymentId );
if ( ! pBaseApp ) continue;
pUpgradeInfo = new UPGRADE_INFO[pBaseApp->_Upgrades + 1]; if ( ! pUpgradeInfo ) { _Status = ERROR_OUTOFMEMORY; return ERROR_OUTOFMEMORY; } memcpy( pUpgradeInfo, pBaseApp->_pUpgrades, pBaseApp->_Upgrades * sizeof(UPGRADE_INFO) );
_pUpgrades[n].pBaseApp = pBaseApp;
if ( _pManApp->GPOList().Compare( _pwszGPOId, pBaseApp->_pwszGPOId ) >= 0 ) { //
// A valid upgrade of the base app. We set a backlink upgrade entry
// for the base app.
//
pUpgradeInfo[pBaseApp->_Upgrades].DeploymentId = _DeploymentId; pUpgradeInfo[pBaseApp->_Upgrades].Flags = (_pUpgrades[n].Flags & ~UPGRADE_OVER) | UPGRADE_BY; pUpgradeInfo[pBaseApp->_Upgrades].pBaseApp = this; } else { //
// An invalid upgrade of the base app because it reverses policy
// precedence. We null out this' upgrade link and set a new
// upgrade link for the base app. The base app becomes the upgrade
// app. The upgrade is forced only if the base app is assigned.
//
// Note that the base app will set a backlink for 'this' in it's own
// InitializePass0 since we process apps from least to highest
// precedence.
//
//
// We preserve the upgrade data for 'this', but remove the forward link flag
// so that it will not be considered to upgrade anything else. We need to
// preserve it so that RSoP logging will be able to log the fact that this
// application upgrades another app
//
_pUpgrades[n].Flags &= ~UPGRADE_OVER; _pUpgrades[n].pBaseApp = NULL;
pUpgradeInfo[pBaseApp->_Upgrades].DeploymentId = _DeploymentId; pUpgradeInfo[pBaseApp->_Upgrades].Flags = UPGRADE_UNINSTALL | UPGRADE_OVER | UPGRADE_REVERSED; if ( pBaseApp->_ActFlags & ACTFLG_Assigned ) pUpgradeInfo[pBaseApp->_Upgrades].Flags |= UPGRADE_FORCE; pUpgradeInfo[pBaseApp->_Upgrades].pBaseApp = this;
DebugMsg((DM_VERBOSE, IDS_UPGRADE_REVERSE, _pwszDeploymentName, _pwszGPOName, pBaseApp->_pwszDeploymentName, pBaseApp->_pwszGPOName)); }
delete pBaseApp->_pUpgrades; pBaseApp->_pUpgrades = pUpgradeInfo; pBaseApp->_Upgrades++; }
return ERROR_SUCCESS; }
void CAppInfo::SetActionPass1() { //
// First pass for setting this app's processing actions. In pass1 we set
// an initial state based solely on the individual app, disregarding at this
// time any interaction with other apps being applied as part of the policy
// run.
//
if ( _pManApp->ARPList() ) { if ( (_ActFlags & (ACTFLG_Assigned | ACTFLG_Published)) && (_ActFlags & ACTFLG_UserInstall) ) { SetAction( ACTION_INSTALL, APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE, NULL); } return; }
if ( _DemandInstall ) { SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE, NULL); }
if ( _pManApp->NoChanges() ) { if ( _State & APPSTATE_ASSIGNED ) { //
// User assigned apps are always readvertised.
// Machine assigned apps get readvertised if uninstalled outside of the scope
// of appmgmt (policy/ARP).
//
if ( _pManApp->IsUserPolicy() || ! AppPresent(_InstallState) ) { SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED, NULL);
_State |= APPSTATE_FULL_ADVERTISE; } } else if ( _State & APPSTATE_PUBLISHED ) { if ( _State & APPSTATE_SCRIPT_NOT_EXISTED ) { //
// This is the roaming case where the app was installed on another
// machine and the user is now logging onto a new machine.
//
SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_PROFILE, NULL); } else { if ( ( (INSTALLSTATE_UNKNOWN == _InstallState) || (INSTALLSTATE_ABSENT == _InstallState) ) && ! _bRestored ) { //
// This is the case where a published app was uninstalled via some
// non-mgmt mechanism like msiexec command line or the app's own
// configuration via ARP. We respect this type of uninstall.
//
SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_PROFILE, NULL);
DebugMsg((DM_VERBOSE, IDS_ORPHAN_ACTION4, _pwszDeploymentName, _pwszGPOName)); } } } return; }
BOOL bUninstalled; DWORD dwRemovalCause;
bUninstalled = (_ActFlags & ACTFLG_Uninstall) && (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED | APPSTATE_SCRIPT_EXISTED));
if ( bUninstalled ) { dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_ADMIN; } else { bUninstalled = (_State & APPSTATE_UNINSTALLED) && (_State & APPSTATE_SCRIPT_EXISTED);
//
// We now know that the app is in the uninstalled state, but the script is here, which means
// that it was uninstalled on another machine if this is user policy. If this app is currently
// assigned in the directory but uninstalled here, that means it was probably upgraded by an app
// on the other machine by an application that no longer applies. In order to get this assigned
// base app to return, we should prevent it from being marked as uninstalled.
//
if ( bUninstalled && _pManApp->IsUserPolicy() && ( _ActFlags & ACTFLG_Assigned ) ) { bUninstalled = FALSE;
DebugMsg((DM_VERBOSE, IDS_ASSIGNED_NOUNINSTALL_ROAM, _pwszDeploymentName, _pwszGPOName)); }
if ( bUninstalled ) { dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_PROFILE; } }
if ( bUninstalled ) { SetAction( ACTION_UNINSTALL, dwRemovalCause, NULL); DebugMsg((DM_VERBOSE, IDS_UNINSTALL_ACTION1, _pwszDeploymentName, _pwszGPOName)); }
if ( ACTION_UNINSTALL == _Action ) return;
if ( ((_ActFlags & ACTFLG_Orphan) && (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED | APPSTATE_SCRIPT_EXISTED))) || ((_State & APPSTATE_ORPHANED) && (_State & APPSTATE_SCRIPT_EXISTED)) ) { SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_ADMIN, NULL);
DebugMsg((DM_VERBOSE, IDS_ORPHAN_ACTION1, _pwszDeploymentName, _pwszGPOName)); }
if ( ACTION_ORPHAN == _Action ) return;
//
// Only look for apply actions if no one else has yet set our status
// explicitly through an upgrade relationship or through the service.
//
// Our first check is for actions to take based on information coming
// down from the directory.
//
if ( ACTION_NONE == _Action ) { if ( _ActFlags & ACTFLG_Assigned ) { if ( (_ActFlags & ACTFLG_InstallUserAssign) && ((_State & APPSTATE_SCRIPT_NOT_EXISTED) || ! (_State & APPSTATE_INSTALL)) ) { //
// This is the new user assigned install option added after Windows2000.
// We do an install just once at each computer. Thereafter it is treated
// as a regular user assignment.
//
SetAction( ACTION_INSTALL, APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED, NULL);
DebugMsg((DM_VERBOSE, IDS_INSTALL_ACTION2, _pwszDeploymentName, _pwszGPOName)); } else if ( _pManApp->IsUserPolicy() || ((_State & APPSTATE_ASSIGNED) && ! AppPresent(_InstallState)) ) { SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED, NULL); _State |= APPSTATE_FULL_ADVERTISE; DebugMsg((DM_VERBOSE, IDS_ASSIGN1_ACTION, _pwszDeploymentName, _pwszGPOName)); } else { //
// We only do an install for a machine assigned app once. After that
// only a redeploy will cause any action.
//
if ( ! (_State & APPSTATE_ASSIGNED) ) { SetAction( ACTION_INSTALL, APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED, NULL);
DebugMsg((DM_VERBOSE, IDS_INSTALL_ACTION1, _pwszDeploymentName, _pwszGPOName)); } } }
//
// We only apply published apps if we're logging onto a machine where
// the published app has not yet been applied.
//
// If the script for the published app exists on the machine then it
// was likely uninstalled via a means we do not detect, so we now
// orphan it.
//
if ( (_ActFlags & ACTFLG_Published) && (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) ) { if ( _State & APPSTATE_SCRIPT_NOT_EXISTED ) { SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_PROFILE, NULL);
DebugMsg((DM_VERBOSE, IDS_ASSIGN3_ACTION, _pwszDeploymentName, _pwszGPOName)); } else if ( _bRestored ) { //
// This is the roaming case where the app was uninstalled on another
// machine because its gpo went out of scope, but on this machine
// that gpo is in scope, so it needs to be readvertised since the
// advertise data was removed on the other machine
//
//
// Note that We set the apply cause value to none which will later force us
// to generate an apply cause that takes into account the apply cause
// currently in the rsop database. Since we do not know at this time what is
// stored in RSoP, we cannot know the correct apply cause so we defer this
// to the time at which we're accessing the database for logging.
//
SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE, NULL);
DebugMsg((DM_VERBOSE, IDS_ASSIGN5_ACTION, _pwszDeploymentName, _pwszGPOName)); } else { if ( (INSTALLSTATE_UNKNOWN == _InstallState) || (INSTALLSTATE_ABSENT == _InstallState) ) { //
// This is the case where a published app was uninstalled via some
// non-mgmt mechanism like msiexec command line or the app's own
// configuration via ARP. We respect this type of uninstall.
//
SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_USER, NULL);
DebugMsg((DM_VERBOSE, IDS_ORPHAN_ACTION4, _pwszDeploymentName, _pwszGPOName)); } else { DebugMsg((DM_VERBOSE, IDS_NONE_ACTION1, _pwszDeploymentName, _pwszGPOName)); } } } }
if ( ACTION_NONE == _Action ) { //
// Three types of apps will still be at ACTION_NONE here :
// + Published apps we already have on the machine
// + Disabled apps -> (ACTFLG_Assigned | ACTFLG_Published) is not set
// + Apps which "disappear" from our policy set because of ACLs
// on the app's deployment properties (not GPO ACLs, that would
// cause a removal of the entire GPO)
//
// We want to do appropriate orphaning actions only for the last
// case. The first two classes of apps are simply left alone. Note
// that if an app is explicitly removed-orphan, we know this
// because it still comes down from the Directory with the
// ACTFLG_Orphan flag set.
//
// We detect the third case with a zero _ActFlag, which means the app
// was not found in the Directory.
//
if ( (0 == _ActFlags) && (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) ) { if ( _State & APPSTATE_POLICYREMOVE_UNINSTALL ) { SetAction( ACTION_UNINSTALL, APP_ATTRIBUTE_REMOVALCAUSE_SCOPELOSS, NULL);
DebugMsg((DM_VERBOSE, IDS_UNINSTALL_ACTION2, _pwszDeploymentName, _pwszGPOName)); } else { SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_SCOPELOSS, NULL); DebugMsg((DM_VERBOSE, IDS_ORPHAN_ACTION3, _pwszDeploymentName, _pwszGPOName)); }
_dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_SCOPELOSS;
return; } }
//
// Check if we have to do a reinstall because of a patch on the install
// image. Note, we only go to this state if the app is already
// installed on this machine.
//
// Note, the Darwin msi database is only cached at install time, so
// if the app is only advertised, we will always pull down the most recent
// msi when the install is invoked.
//
// Also, the INSTALLSTATE_DEFAULT covers both the INSTALLSTATE_LOCAL and
// INSTALLSTATE_SOURCE.
//
if ( (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) && (_State & APPSTATE_SCRIPT_EXISTED) && (_DirectoryRevision > 0) ) { CAppInfo * pScriptInfo;
if ( _LocalRevision < _DirectoryRevision ) { SetAction( ACTION_REINSTALL, APP_ATTRIBUTE_APPLYCAUSE_VALUE_REDEPLOY, NULL);
DebugMsg((DM_VERBOSE, IDS_REINSTALL_ACTION1, _pwszDeploymentName, _pwszGPOName)); } else { pScriptInfo = _pManApp->ScriptList().Find( _DeploymentId );
if ( CompareFileTime( &pScriptInfo->_ScriptTime, &_ScriptTime ) < 0 ) { SetAction( ACTION_REINSTALL, APP_ATTRIBUTE_APPLYCAUSE_VALUE_REDEPLOY, NULL);
if ( DebugLevelOn(DM_VERBOSE) ) { SYSTEMTIME LocalTime; SYSTEMTIME SysvolTime; WCHAR wszLocalTime[32]; WCHAR wszSysvolTime[32];
FileTimeToSystemTime( &pScriptInfo->_ScriptTime, &LocalTime ); FileTimeToSystemTime( &_ScriptTime, &SysvolTime );
(void) StringCchPrintf( wszLocalTime, sizeof(wszLocalTime)/sizeof(wszLocalTime[0]), L"%02d-%02d %02d:%02d:%02d:%03d", LocalTime.wMonth, LocalTime.wDay, LocalTime.wHour, LocalTime.wMinute, LocalTime.wSecond, LocalTime.wMilliseconds );
(void) StringCchPrintf( wszSysvolTime, sizeof(wszSysvolTime)/sizeof(wszSysvolTime[0]), L"%02d-%02d %02d:%02d:%02d:%03d", SysvolTime.wMonth, SysvolTime.wDay, SysvolTime.wHour, SysvolTime.wMinute, SysvolTime.wSecond, SysvolTime.wMilliseconds );
DebugMsg((DM_VERBOSE, IDS_REINSTALL_ACTION2, _pwszDeploymentName, _pwszGPOName, wszLocalTime, wszSysvolTime)); } } } } }
void CAppInfo::SetActionPass2() { //
// In pass two we do product id filtering based on language and policy
// precedence.
//
//
// In reporting mode, we perform no processing, all apps that currently
// apply (i.e. all apps in the gpo) will be logged
//
if ( _pManApp->GetRsopContext()->IsReportingModeEnabled() ) { return; }
if ( (ACTION_UNINSTALL == _Action) || (ACTION_ORPHAN == _Action) ) return;
//
// When creating the list of apps to show in ARP we want to include
// the highest precedence assigned app (if one exists) and all
// published apps from higher precedence policies then the one that
// the (optional) assigned app has come from. Thus if there are no
// assigned apps with the product code, we would show the published
// apps from all policies.
//
// This is done simply by not allowing published apps to filter out
// any lower precedence apps.
//
if ( _pManApp->ARPList() && (_ActFlags & ACTFLG_Published) ) return;
//
// Published apps which we already have on the machine will never be
// set to apply again. However, we still want them to override lower
// precedence products (even assigned) that have the same product id.
// So the below check causes the logic to continue as long as 'this'
// app has already been applied on this machine.
//
// Note that we check for the assigned state as well because it could
// be getting changed to published in this run of policy.
//
if ( (ACTION_NONE == _Action) && ! (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) ) return;
//
// If multiple apps of the same product id are set to be assigned or
// installed, only assign/install the one in the closest GPO. This is
// usefull when the app is deployed with a different set of transforms
// in the different GPOs. Darwin will only honor one set of transforms.
//
//
// We do this pass in the NoChanges case to enforce the product code
// filtering logic.
//
CAppInfo * pAppInfo; DWORD n; BOOL bPastThis; BOOL bAnalyzeForRsopOnly;
bPastThis = FALSE; bAnalyzeForRsopOnly = FALSE;
for ( _pManApp->AppList().Reset(), pAppInfo = (CAppInfo *) _pManApp->AppList().GetCurrentItem(); pAppInfo; _pManApp->AppList().MoveNext(), pAppInfo = (CAppInfo *) _pManApp->AppList().GetCurrentItem() ) { if ( pAppInfo == this ) { bPastThis = TRUE; continue; }
//
// Once we're past 'this' we need to start checking if we should
// stop processing. We don't want to supercede an app with the
// same product id which is of higher precedence.
//
// When not constructing the ARP list of apps, we stop once
// past 'this'. The most recently deployed app will win ties in
// that case.
//
// When constructing the ARP list of apps, we keep on looking at other
// apps until we encounter a new policy or an assigned app. That is
// because we want to treat all published apps in a policy equally,
// but an assigned app acts as a blocking point.
//
if ( bPastThis ) { if ( lstrcmpi( pAppInfo->_pwszGPOId, _pwszGPOId ) != 0 ) break;
if ( ! _pManApp->ARPList() ) break; else if ( pAppInfo->_ActFlags & ACTFLG_Assigned ) bAnalyzeForRsopOnly = TRUE; }
if ( (ACTION_ORPHAN == pAppInfo->_Action) || (ACTION_UNINSTALL == pAppInfo->_Action) ) continue;
if ( (ACTION_NONE == pAppInfo->_Action) && ! (pAppInfo->_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) && ! (pAppInfo->_ActFlags & (ACTFLG_Assigned | ACTFLG_Published)) ) continue;
if ( lstrcmpi( pAppInfo->_pwszProductId, _pwszProductId ) != 0 ) continue;
//
// One important exception to product code filtering is when there are
// upgrades. We don't do any filtering in that case. It doesn't matter
// whether this is an "upgrade by" or "upgrade over" upgrade.
//
for ( n = 0; n < _Upgrades; n++ ) { if ( _pUpgrades[n].pBaseApp == pAppInfo ) break; }
if ( n < _Upgrades ) continue;
//
// Now we know we want to override the lower precedence app so we
// set it to orphan. We don't want to do an uninstall, if a
// tranform conflict requires this, that will be detected later.
// Note that if the lower precedence app is not already present on
// the machine then it's action will be changed to ACTION_NONE in
// Pass4. But we still set it to ACTION_ORPHAN here so that none of
// it's upgrade settings will be applied in it's Pass3.
//
// Within a single GPO, language match takes precedence over last
// modified time.
//
if ( (pAppInfo->_LanguageWeight > _LanguageWeight) && (0 == lstrcmpi( pAppInfo->_pwszGPOId, _pwszGPOId )) ) { if ( ! bAnalyzeForRsopOnly ) { SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_SCOPELOSS, pAppInfo); DebugMsg((DM_VERBOSE, IDS_UNDO3_ACTION, _pwszDeploymentName, _pwszGPOName, pAppInfo->_pwszDeploymentName, pAppInfo->_pwszGPOName)); }
(void) pAppInfo->UpdatePrecedence( this, APP_ATTRIBUTE_REASON_VALUE_LANGUAGE );
bAnalyzeForRsopOnly = TRUE; } else { if ( ! bAnalyzeForRsopOnly ) { pAppInfo->SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_PRODUCT, this);
DebugMsg((DM_VERBOSE, IDS_UNDO6_ACTION, pAppInfo->_pwszDeploymentName, pAppInfo->_pwszGPOName, _pwszDeploymentName, _pwszGPOName)); }
(void) UpdatePrecedence( pAppInfo, APP_ATTRIBUTE_REASON_VALUE_PRODUCT ); } }
_pManApp->AppList().ResetEnd(); }
void CAppInfo::SetActionPass3() { static const UCHAR PolicyForceBaseNone[BASE_STATE_CHOICES][UPGRADE_STATE_CHOICES] = { { NO, NO, NO, NO }, { SPECIAL1, SPECIAL1, NO, NO }, { NO, NO, NO, NO }, { NO, NO, NO, NO } }; static const UCHAR PolicyForceUpgradeNone[BASE_STATE_CHOICES][UPGRADE_STATE_CHOICES] = { { NO, NO, NO, NO }, { SPECIAL2, SPECIAL2, NO, NO }, { YES, SPECIAL2, NO, NO }, { YES, SPECIAL2, NO, NO } }; static const UCHAR PolicyApplyUpgrade[BASE_STATE_CHOICES][UPGRADE_STATE_CHOICES] = { { NO, NO, NO, NO }, { NO, NO, YES, YES }, { NO, SPECIAL1, YES, YES }, { NO, SPECIAL1, YES, YES } }; static const UCHAR ARPForceBaseNone[BASE_STATE_CHOICES][UPGRADE_STATE_CHOICES] = { { SPECIAL1, SPECIAL1, YES, YES }, { NO, NO, YES, YES }, { NO, NO, YES, YES }, { NO, NO, YES, YES } };
//
// In pass three, we may modify this app's or other app's action based upon
// upgrade relationships between the set of apps in a policy run.
//
//
// In reporting mode, we perform no processing, all apps that currently
// apply (i.e. all apps in the gpo) will be logged
//
if ( _pManApp->GetRsopContext()->IsReportingModeEnabled() ) { return; }
if ( _pManApp->NoChanges() ) return;
if ( ! _Upgrades ) return;
BOOL bAnalyzeForRsopOnly;
bAnalyzeForRsopOnly = FALSE;
if ( (ACTION_UNINSTALL == _Action) || (ACTION_ORPHAN == _Action) ) { //
// Even if this app is not applied, we want to be sure that
// it doesn't have some upgrades
//
if ( _pManApp->GetRsopContext()->IsRsopEnabled() ) { bAnalyzeForRsopOnly = TRUE; } else { return; } }
if ( ! (_ActFlags & (ACTFLG_Assigned | ACTFLG_Published)) ) return;
for ( DWORD n = 0; n < _Upgrades; n++ ) { CAppInfo * pBaseApp; DWORD BaseIndex; DWORD UpgradeIndex; BOOL bBasePresent; BOOL bBaseToApply; BOOL bUpgradePresent; BOOL bUpgradeForced; BOOL bUpgradeToApply; BOOL bApplyUpgrade;
if ( ! (_pUpgrades[n].Flags & UPGRADE_OVER) ) continue;
pBaseApp = _pUpgrades[n].pBaseApp;
if ( ! pBaseApp ) continue;
//
// Notes about the state settings :
//
// BasePresent should be set to FALSE if the app is set to be unmanaged and the
// upgrade is not forced. This is to ensure the if the upgrade app is assigned
// it will be applied. Likewise, if the upgrade is forced then we want to treat
// the base app as present if it was currently managed. That is to ensure that
// the upgrade will apply even if the base app is already set to be removed.
//
//
// Four conditions under which an upgrade is forced :
// 1. During an install from ARP or fileext/clsid activation.
// 2. If the app is configured to force it's upgrades.
// 3. If the upgrade link was reversed because of policy precedence (see InitializePass0)
// 4. Whenever an app is restored after a profile sync problem (see CManagedAppProcessor::GetLostApps)
//
bUpgradeForced = _DemandInstall || (_ActFlags & ACTFLG_ForceUpgrade) || (_pUpgrades[n].Flags & UPGRADE_FORCE) || (_State & APPSTATE_RESTORED);
bBasePresent = ( pBaseApp->_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED) );
if ( ! _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { if ( ! bUpgradeForced && ((ACTION_ORPHAN == pBaseApp->_Action) || (ACTION_UNINSTALL == pBaseApp->_Action)) ) bBasePresent = FALSE; } else if ( CRsopAppContext::POLICY_REFRESH == _pManApp->GetRsopContext()->GetContext() ) { if ( ! ( ( _ActFlags & ACTFLG_Assigned ) && ! ( pBaseApp->_ActFlags & ACTFLG_Assigned ) ) ) { bBasePresent = TRUE; } else { bBasePresent = FALSE; } }
if ( _pManApp->ARPList() ) bBaseToApply = pBaseApp->_State & APPSTATE_ASSIGNED; else bBaseToApply = (ACTION_APPLY == pBaseApp->_Action) || (ACTION_INSTALL == pBaseApp->_Action);
bUpgradePresent = _State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED);
if ( _pManApp->ARPList() ) bUpgradeToApply = _State & APPSTATE_ASSIGNED; else bUpgradeToApply = (ACTION_APPLY == _Action) || (ACTION_INSTALL == _Action);
if ( _pManApp->GetRsopContext()->IsPlanningModeEnabled() && ( CRsopAppContext::ARPLIST == _pManApp->GetRsopContext()->GetContext() ) ) { if ( ( _ActFlags & ACTFLG_Assigned ) && ! ( pBaseApp->_ActFlags & ACTFLG_Assigned ) ) { bUpgradeToApply = TRUE; bUpgradePresent = TRUE; } }
DebugMsg((DM_VERBOSE, IDS_UPGRADE_INFO, _pwszDeploymentName, _pwszGPOName, pBaseApp->_pwszDeploymentName, pBaseApp->_pwszGPOName, bBasePresent, bBaseToApply, bUpgradePresent, bUpgradeToApply, bUpgradeForced));
if ( bBasePresent ) BaseIndex = bBaseToApply ? BASE_STATE_PRESENT_APPLY : BASE_STATE_PRESENT_NOTAPPLY; else BaseIndex = bBaseToApply ? BASE_STATE_ABSENT_APPLY : BASE_STATE_ABSENT_NOTAPPLY;
if ( bUpgradeForced ) UpgradeIndex = bUpgradeToApply ? UPGRADE_STATE_FORCED_APPLY : UPGRADE_STATE_FORCED_NOTAPPLY; else UpgradeIndex = bUpgradeToApply ? UPGRADE_STATE_NOTFORCED_APPLY : UPGRADE_STATE_NOTFORCED_NOTAPPLY;
//
// In the case where we are constructing the list to show in ARP, the
// only decision is whether to hide the base app.
//
if ( _pManApp->ARPList() ) { if ( (YES == ARPForceBaseNone[BaseIndex][UpgradeIndex]) || ((SPECIAL1 == ARPForceBaseNone[BaseIndex][UpgradeIndex]) && bUpgradePresent) ) { if ( ! bAnalyzeForRsopOnly ) { DebugMsg((DM_VERBOSE, IDS_UNDO4_ACTION, pBaseApp->_pwszDeploymentName, pBaseApp->_pwszGPOName, _pwszDeploymentName, _pwszGPOName));
pBaseApp->SetAction( ACTION_NONE, APP_ATTRIBUTE_REMOVALCAUSE_NONE, this); }
(void) UpdatePrecedence( pBaseApp, APP_ATTRIBUTE_REASON_VALUE_UPGRADE ); } continue; }
//
// The rest of this code executes when we are running policy in winlogon
// or when doing a demand install.
//
if ( (YES == PolicyForceBaseNone[BaseIndex][UpgradeIndex]) || ((SPECIAL1 == PolicyForceBaseNone[BaseIndex][UpgradeIndex]) && bUpgradePresent) ) { if ( ! bAnalyzeForRsopOnly ) { DebugMsg((DM_VERBOSE, IDS_UNDO1_ACTION, pBaseApp->_pwszDeploymentName, pBaseApp->_pwszGPOName, _pwszDeploymentName, _pwszGPOName)); pBaseApp->SetAction( ACTION_NONE, APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE, this); } }
if ( (YES == PolicyForceUpgradeNone[BaseIndex][UpgradeIndex]) || ((SPECIAL2 == PolicyForceUpgradeNone[BaseIndex][UpgradeIndex]) && ! bUpgradePresent) ) { if ( ! bAnalyzeForRsopOnly ) { DebugMsg((DM_VERBOSE, IDS_UNDO2_ACTION, _pwszDeploymentName, _pwszGPOName, pBaseApp->_pwszDeploymentName, pBaseApp->_pwszGPOName)); SetAction( ACTION_NONE, APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE, pBaseApp); }
(void) pBaseApp->UpdatePrecedence( this, APP_ATTRIBUTE_REASON_VALUE_NONFORCEDUPGRADE ); }
bApplyUpgrade = (YES == PolicyApplyUpgrade[BaseIndex][UpgradeIndex]) || ((SPECIAL1 == PolicyApplyUpgrade[BaseIndex][UpgradeIndex]) && bUpgradePresent);
if ( ! bApplyUpgrade ) { if ( _pManApp->GetRsopContext()->IsPlanningModeEnabled() && ( CRsopAppContext::POLICY_REFRESH == _pManApp->GetRsopContext()->GetContext() ) && ( ( ACTION_APPLY == _Action ) || ( ACTION_INSTALL == _Action ) ) ) { UpdatePrecedence( pBaseApp, APP_ATTRIBUTE_REASON_VALUE_UPGRADE ); }
continue; }
//
// At this point we know that this upgrade relationship is set to be
// applied.
//
(void) UpdatePrecedence( pBaseApp, APP_ATTRIBUTE_REASON_VALUE_UPGRADE );
if ( _pManApp->GetRsopContext()->IsRsopEnabled() ) { if ( bBasePresent ) { _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_UPGRADE; } }
//
// If we are only here to ensure that we get all the upgrades for RSoP,
// we should leave now since we know that the upgrade is enforced
//
if ( bAnalyzeForRsopOnly ) { return; }
//
// Make sure a full advertise is done for the new app during user policy
// processing.
// We don't want this for machine policy, user assign full install option
// nor ARP since the install action will follow in that case.
//
if ( _pManApp->IsUserPolicy() && ! (_ActFlags & ACTFLG_InstallUserAssign) && ! _DemandInstall ) _State |= APPSTATE_FULL_ADVERTISE;
//
// This is for the case when the new upgrade app is published.
//
if ( ACTION_NONE == _Action ) { SetAction( ACTION_APPLY, APP_ATTRIBUTE_APPLYCAUSE_VALUE_UPGRADE, NULL);
DebugMsg((DM_VERBOSE, IDS_ASSIGN4_ACTION, _pwszDeploymentName, _pwszGPOName)); }
if ( ! bBasePresent ) {
DebugMsg((DM_VERBOSE, IDS_UNDO4_ACTION, pBaseApp->_pwszDeploymentName, pBaseApp->_pwszGPOName, _pwszDeploymentName, _pwszGPOName)); pBaseApp->_Action = ACTION_NONE; continue; }
if ( ! _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { //
// Before we start setting the upgrade apps' states, we need to make
// sure that we can get the new app's script from the sysvol. Because
// of replication issues, it's possible the script may not be available
// on the DC we bind to. We don't want to undo the upgraded apps until
// we know we'll be able to apply the new one.
//
_Status = CopyScriptIfNeeded(); if ( _Status != ERROR_SUCCESS ) { //
// This may seem strange, but logging all three of these events provides
// the proper context of the error. This is how a failed upgrade would
// normally be logged, but in this case the error occurs so early we
// abort before the normal set of processing is performed that would log
// the other events. We want to stop early to prevent doing unnecessary
// uninstalls.
//
gpEvents->Upgrade( this, pBaseApp, _pUpgrades[n].Flags & UPGRADE_UNINSTALL ); gpEvents->Assign( _Status, this ); gpEvents->UpgradeAbort( _Status, this, pBaseApp, FALSE ); SetAction( ACTION_NONE, _dwApplyCause, NULL);
pBaseApp->_bRollback = TRUE;
return; } }
_pUpgrades[n].Flags |= UPGRADE_APPLIED;
if ( ! _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { if ( _pUpgrades[n].Flags & UPGRADE_UNINSTALL ) { pBaseApp->SetAction( ACTION_UNINSTALL, APP_ATTRIBUTE_REMOVALCAUSE_UPGRADE, this);
gpEvents->Upgrade( this, pBaseApp, TRUE ); } else // _pUpgrades[n].Flags & UPGRADE_NOUNINSTALL
{ pBaseApp->SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_UPGRADE, this);
gpEvents->Upgrade( this, pBaseApp, FALSE ); } } } }
void CAppInfo::SetActionPass4() { //
// In pass four we look for various cases of simultaneous advertise and
// unadvertise of the same product or other action states that need
// slight changes based on all of the inter-deployment processing we've
// finished.
//
//
// In reporting mode, we perform no processing, all apps that currently
// apply (i.e. all apps in the gpo) will be logged
//
if ( _pManApp->GetRsopContext()->IsReportingModeEnabled() ) { return; }
//
// If this product is to be assigned, check if the same product is being
// uninstalled now as well. If so, we switch this app to be applied async,
// so that it will get assigned again after the uninstall action.
//
CAppInfo * pAppInfo;
//
// If an app is set to orphan, but we don't even have it in the registry,
// then we can just fall back to do nothing.
// This can happen for upgrades of assigned apps that were previously
// applied.
//
if ( ACTION_ORPHAN == _Action ) { if ( ! (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED | APPSTATE_SCRIPT_EXISTED)) ) { SetAction( ACTION_NONE, APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE, NULL);
DebugMsg((DM_VERBOSE, IDS_NONE_ACTION2, _pwszDeploymentName, _pwszGPOName)); } }
for ( _pManApp->AppList().Reset(), pAppInfo = (CAppInfo *) _pManApp->AppList().GetCurrentItem(); pAppInfo; _pManApp->AppList().MoveNext(), pAppInfo = (CAppInfo *) _pManApp->AppList().GetCurrentItem() ) { if ( pAppInfo == this ) break;
if ( lstrcmpi( pAppInfo->_pwszProductId, _pwszProductId ) != 0 ) continue;
//
// When the lower precedent app is set to be uninstalled, we switch it
// to orphan if the higher precedent app is already on the machine.
// This holds even if the higher precedent app is set to be orphaned
// or uninstalled.
//
if ( ACTION_UNINSTALL == pAppInfo->_Action ) { if ( _State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED) ) { DebugMsg((DM_VERBOSE, IDS_UNDO5_ACTION, pAppInfo->_pwszDeploymentName, pAppInfo->_pwszGPOName, _pwszDeploymentName, _pwszGPOName));
pAppInfo->SetAction( ACTION_ORPHAN, APP_ATTRIBUTE_REMOVALCAUSE_PRODUCT, this); } } }
_pManApp->AppList().ResetEnd(); }
void CAppInfo::SetAction( APPACTION AppAction, DWORD Reason, CAppInfo* pAppCause ) { _Action = AppAction;
if ( ( _Action == ACTION_APPLY ) || ( _Action == ACTION_INSTALL ) || ( _Action == ACTION_REINSTALL) ) { _dwApplyCause = Reason;
} else if ( ( _Action == ACTION_ORPHAN ) || ( _Action == ACTION_UNINSTALL ) ) { _dwRemovalCause = Reason;
if ( pAppCause ) { HRESULT hr;
hr = SetRemovingDeploymentId( &(pAppCause->_DeploymentId) );
if ( FAILED( hr ) ) { _pManApp->GetRsopContext()->DisableRsop( hr ); } } } else { _dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_NONE; _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE; } }
DWORD CAppInfo::ProcessApplyActions() { DWORD n; DWORD ScriptFlags;
if ( _Status != ERROR_SUCCESS ) return _Status;
switch ( _Action ) { case ACTION_NONE : //
// If policy has changed, then we want to rewrite our state even for apps
// which we have but which are not set to apply. This is to update any
// settings like UI level or orphan/uninstall at policy removal.
//
if ( ! _pManApp->NoChanges() && (_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) ) (void) Assign( 0, FALSE, TRUE ); break; case ACTION_APPLY : //
// Note that apply actions can be processed during an async refresh, as long as they
// do not lead to an install
//
case ACTION_INSTALL : case ACTION_REINSTALL : BOOL bUpgradeComplete;
bUpgradeComplete = FALSE;
if ( ! _DemandInstall ) { //
// Before applying an app which is an upgrade of something
// already on the machine, we make sure that all necessary actions
// on the old apps completed successfully. If there were any errors in
// the uninstalls, then the upgrade app is not applied.
// Upgrade processed through ARP actions are transacted differently
// since they involve an install of the new app, so this does not
// apply in that case.
//
for ( n = 0; n < _Upgrades; n++ ) { if ( ! _pUpgrades[n].pBaseApp || ! (_pUpgrades[n].Flags & UPGRADE_OVER) ) continue;
// Abort and fail if any of the upgraded app removals failed.
if ( _pUpgrades[n].pBaseApp->_Status != ERROR_SUCCESS ) { DWORD retStatus = RollbackUpgrades(); if ( retStatus == ERROR_SUCCESS ) { retStatus = _pUpgrades[n].pBaseApp->_Status; } return retStatus; }
// Remember if any of the base apps were actually present.
if ( _pUpgrades[n].pBaseApp->_State & (APPSTATE_PUBLISHED | APPSTATE_ASSIGNED) ) bUpgradeComplete = TRUE; } }
ScriptFlags = 0;
//
// Reinstalls require recopying the script file because the msi
// data may have been modified in a way which changes the script
// data.
// Also check for a first time advertise where we need to update
// the registry stored script time.
//
if ( (ACTION_REINSTALL == _Action) || ((_DirectoryRevision > 0) && (0 == _ScriptTime.dwLowDateTime) && (0 == _ScriptTime.dwHighDateTime)) ) { WIN32_FIND_DATA FindData; HANDLE hFind;
hFind = FindFirstFile( _pwszGPTScriptPath, &FindData ); if ( INVALID_HANDLE_VALUE == hFind ) { _Status = GetLastError(); gpEvents->Reinstall( _Status, this ); return _Status; }
FindClose( hFind ); _ScriptTime = FindData.ftLastWriteTime;
if ( ACTION_REINSTALL == _Action ) { // This will force the script to be recopied.
_State &= ~APPSTATE_SCRIPT_PRESENT;
//
// Force a full readvertise for assigned apps. This catches handles cases where the app
// is only in an advertised state.
//
if ( _ActFlags & ACTFLG_Assigned ) ScriptFlags = SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS | SCRIPTFLAGS_REGDATA_EXTENSIONINFO; } }
//
// Determine whether this app will require uninstall of an
// existing unmanaged version of the same application -- note that
// this call must be made while impersonating
//
_bNeedsUnmanagedRemove = RequiresUnmanagedRemoval();
if ( _Action != ACTION_REINSTALL ) _Status = Assign( ScriptFlags, TRUE, TRUE ); else _Status = Assign( ScriptFlags, TRUE, FALSE );
if ( ERROR_INSTALL_TRANSFORM_FAILURE == _Status ) { DebugMsg((DM_VERBOSE, IDS_INSTALL_TRANSFORM, _pwszDeploymentName, _pwszGPOName)); _State |= APPSTATE_TRANSFORM_CONFLICT;
_bTransformConflict = TRUE;
//
// Though we are not really doing an install, the uninstall is an operation
// we are performing in order to apply this new deployment of the app. We
// don't want to scare the user into thinking their app is being
// totally nuked. So in this case we actually put up the install message.
//
_pManApp->LogonMsgInstall( _pwszDeploymentName ); _Status = Uninstall();
if ( ERROR_SUCCESS == _Status ) { _Status = Assign(); }
_pManApp->LogonMsgApplying(); } else if ( ERROR_SUCCESS != _Status ) { //
// Ensure that we record an event in this case
// so RSoP will associate an error with this application
//
if ( _Action == ACTION_REINSTALL ) { gpEvents->Assign( _Status, this ); } }
//
// If a script is not present, we should check for an unmanaged version of the app if it's going to be applied,
// since we will want to reinstall if this app is not configured to remove unmanaged installs
// of the same app.
//
// This can also happen if the application temporarily goes out of scope on this machine
// due to wql filtering or security group filtering, and then comes back into scope.
//
//
// Note that we do this now rather than in SetActions because we want to wait until
// all unapply actions have taken place before we make this decision
//
if ( ! _DemandInstall && ( ACTION_APPLY == _Action || ACTION_INSTALL == _Action ) && ( _State & APPSTATE_SCRIPT_NOT_EXISTED ) && ! _bNeedsUnmanagedRemove ) { DWORD InstallState;
//
// We must query the install state since we normally only query
// the install state when the script exists
//
InstallState = gpfnMsiQueryProductState( _pwszProductId );
if ( ( INSTALLSTATE_DEFAULT == InstallState ) || ( INSTALLSTATE_LOCAL == InstallState ) ) { SetAction( ACTION_REINSTALL, _dwApplyCause, NULL);
DebugMsg((DM_VERBOSE, IDS_REINSTALL_ACTION3, _pwszDeploymentName, _pwszGPOName)); } }
if ( ERROR_SUCCESS == _Status ) { if ( ACTION_INSTALL == _Action ) { _pManApp->LogonMsgInstall( _pwszDeploymentName ); _Status = Install(); _pManApp->LogonMsgApplying(); } else if ( ACTION_REINSTALL == _Action ) { //
// Note that in the redeploy case, the full reinstall is needed only if the app
// has already been installed once on this machine.
// We want to check for any unmanaged install instance, so don't use
// _InstallState here.
//
if ( INSTALLSTATE_DEFAULT == (*gpfnMsiQueryProductState)( _pwszProductId ) ) { _pManApp->LogonMsgInstall( _pwszDeploymentName ); _Status = Reinstall(); _pManApp->LogonMsgApplying(); }
//
// Make sure to do this even if we didn't attempt a reinstall so that
// any new properites of the app get written to our local key.
//
if ( ERROR_SUCCESS == _Status ) _Status = Assign( 0, FALSE, TRUE ); } }
if ( (ACTION_INSTALL == _Action) && (_Status != ERROR_SUCCESS) && (_State & APPSTATE_SCRIPT_NOT_EXISTED) ) { //
// Try to rollback the failed install
//
Unassign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO, TRUE ); }
if ( bUpgradeComplete ) { if ( _Status != ERROR_SUCCESS ) { // Ignoring result from RollbackUpgrades since it does not modify _Status, which
// will be returned by this function and is not equal to ERROR_SUCCESS to begin with.
(void) RollbackUpgrades(); break; }
for ( n = 0; n < _Upgrades; n++ ) { if ( ! _pUpgrades[n].pBaseApp || ! (_pUpgrades[n].Flags & UPGRADE_OVER) ) continue;
if ( _pUpgrades[n].pBaseApp->_State & (APPSTATE_PUBLISHED | APPSTATE_ASSIGNED) ) gpEvents->UpgradeComplete( this, _pUpgrades[n].pBaseApp ); } } break; case ACTION_UNINSTALL : case ACTION_ORPHAN : break; }
return _Status; }
DWORD CAppInfo::ProcessUnapplyActions() { if ( _Status != ERROR_SUCCESS ) return _Status;
switch ( _Action ) { case ACTION_NONE : case ACTION_APPLY : case ACTION_INSTALL : case ACTION_REINSTALL : break; case ACTION_UNINSTALL :
//
// During async refreshes, we will not process any unapply actions as that
// would be disruptive to the user. This also prevents rip n replace upgrades,
// since an unapply action is required for the base app and this will set the error
// for the base app so that the upgrade app is not assigned.
//
if ( _pManApp->Async() ) { DebugMsg((DM_VERBOSE, IDS_ABORT_OPERATION)); _Status = ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED; break; }
if ( _State & APPSTATE_SCRIPT_NOT_EXISTED ) { INSTALLSTATE InstallState;
//
// This is a bizarre scenario possible with a roaming profile.
// It's possible we may be getting the uninstall action for the
// first time while logging onto a machine where an assignment of
// the app has never been done.
// In that case we make our best effort to remove advertise data that
// may be in the profile. We don't want to do any uninstall action
// here since we never performed an install action. Note we don't
// use SCRIPTFLAG_REGDATA_APPINFO because class registrations don't
// roam. If we can't get the script to do the unadvertise, we continue
// and unmanage the app.
//
//
// Note that we only want to unadvertise if the app is in the
// advertised state or not present -- otherwise, we may trash
// an unmanaged version of the app, or a managed version of the application
// with the same product id. We check the install state below -- we must
// call the api to do this since our _InstallState member is only initialized
// when the script exists, and in this case we know it does not
//
InstallState = (*gpfnMsiQueryProductState)( _pwszProductId );
if ( ( INSTALLSTATE_ADVERTISED == InstallState ) || ( INSTALLSTATE_ABSENT == InstallState ) ) { //
// Artifically bump up the ref count since an assignment was never done
// on this machine.
//
if ( _pManApp->IsUserPolicy() ) _AssignCount++;
if ( ERROR_SUCCESS == CopyScriptIfNeeded() ) _Status = Unassign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS, FALSE ); } } else { _pManApp->LogonMsgUninstall( _pwszDeploymentName ); _Status = Uninstall( FALSE ); _pManApp->LogonMsgApplying();
//
// If an app is only advertised, then an uninstall will fail with
// ERROR_INSTALL_SOURCE_ABSENT if the original package source (msi)
// can not be accessed. This is because the msi is not cached until
// install time.
// Uninstall will also fail if the package has been disallowed through
// software restriction policies.
// In these cases, we just need to undo the rest of the advertise that
// we did originally.
//
if ( (INSTALLSTATE_ADVERTISED == _InstallState) && ((ERROR_INSTALL_SOURCE_ABSENT == _Status) || (ERROR_INSTALL_PACKAGE_REJECTED == _Status) || (ERROR_INSTALL_TRANSFORM_REJECTED == _Status) || (ERROR_INSTALL_TRANSFORM_FAILURE == _Status)) ) { _Status = Unassign( SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS | SCRIPTFLAGS_REGDATA_EXTENSIONINFO, FALSE ); }
if ( ERROR_SUCCESS != _Status ) { gpEvents->Uninstall( _Status, this ); } }
//
// On success we can finally remove our own internal state info
// about this apps.
//
if ( ERROR_SUCCESS == _Status ) _Status = Unassign(); break; case ACTION_ORPHAN :
//
// Artifically bump up the ref count since an assignment was never done
// on this machine.
//
if ( (_State & APPSTATE_SCRIPT_NOT_EXISTED) && _pManApp->IsUserPolicy() ) _AssignCount++;
_Status = Unassign();
break; }
return _Status; }
DWORD CAppInfo::ProcessTransformConflicts() { //
// For RSoP, we need to say which apps were uninstalled due
// to transform conflicts
//
//
// Since the uninstall was performed as part of installing an app
// in PRocessApplyActions (upon receiving ERROR_INSTALL_TRANSFORM_FAILURE
// from MsiAdvertiseScript), any installed apps would have had the same
// product id and therefore would have been marked to uninstall in a
// previous pass
//
//
// So first we check to see if this app encountered a transform
// conflict when it was installed
//
if ( _bTransformConflict ) { //
// Now look for any application with the same product id marked
// as being removed due to product conflict:
//
CAppInfo* pAppInfo;
for ( _pManApp->AppList().Reset(), pAppInfo = (CAppInfo *) _pManApp->AppList().GetCurrentItem(); pAppInfo; _pManApp->AppList().MoveNext(), pAppInfo = (CAppInfo *) _pManApp->AppList().GetCurrentItem() ) { //
// Check to see if the current app was removed due to product conflict
//
if ( APP_ATTRIBUTE_REMOVALCAUSE_PRODUCT != pAppInfo->_dwRemovalCause ) continue;
//
// See if the current app's product id matches
//
if ( lstrcmpi( pAppInfo->_pwszProductId, _pwszProductId ) != 0 ) continue;
//
// Since this app had a product conflict with the current app,
// it had to have been uninstalled as part of installing this app,
// so we note this
//
pAppInfo->_dwRemovalCause = APP_ATTRIBUTE_REMOVALCAUSE_TRANSFORM; }
_pManApp->AppList().ResetEnd(); }
return ERROR_SUCCESS; }
DWORD CAppInfo::CopyToManagedApplication( MANAGED_APP * pManagedApp ) { DWORD cbApplication;
LONG Error;
Error = ERROR_SUCCESS;
//
// Copy guid data
//
StringToGuid(_pwszGPOId, &(pManagedApp->GpoId)); StringToGuid(_pwszProductId, &(pManagedApp->ProductId));
//
// Copy simple data
//
pManagedApp->dwVersionHi = _VersionHi; pManagedApp->dwVersionLo = _VersionLo; pManagedApp->dwRevision = _LocalRevision; pManagedApp->Language = _LangId; pManagedApp->pszOwner = NULL; pManagedApp->pszCompany = NULL; pManagedApp->pszComments = NULL; pManagedApp->pszContact = NULL;
//
// Copy information about the application type -- do
// a translation from com pathtype constants to Win32
// constants.
//
switch ( _PathType ) { case DrwFilePath: pManagedApp->dwPathType = MANAGED_APPTYPE_WINDOWSINSTALLER; break;
case SetupNamePath: pManagedApp->dwPathType = MANAGED_APPTYPE_SETUPEXE; break;
default: pManagedApp->dwPathType = MANAGED_APPTYPE_UNSUPPORTED; }
pManagedApp->bInstalled = ((_State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED)) != 0);
//
// Copy string data
//
if (_pwszDeploymentName) { pManagedApp->pszPackageName = MidlStringDuplicate(_pwszDeploymentName);
if (!(pManagedApp->pszPackageName)) { Error = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } }
if (_pwszGPOName) { pManagedApp->pszPolicyName = MidlStringDuplicate(_pwszGPOName);
if (!(pManagedApp->pszPolicyName)) { Error = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } }
if (_pwszPublisher) { pManagedApp->pszPublisher = MidlStringDuplicate(_pwszPublisher);
if (!(pManagedApp->pszPublisher)) { Error = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } }
if (_pwszSupportURL) { pManagedApp->pszSupportUrl = MidlStringDuplicate(_pwszSupportURL);
if (!(pManagedApp->pszSupportUrl)) { Error = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } }
cleanup:
if (ERROR_SUCCESS != Error) { ClearManagedApp( pManagedApp ); }
return Error; }
BOOL CAppInfo::HasCategory( WCHAR * pwszCategory ) { for ( DWORD n = 0; n < _cCategories; n++ ) { if ( 0 == lstrcmp( _rgwszCategories[n], pwszCategory ) ) return TRUE; }
return FALSE; }
DWORD CAppInfo::Assign( DWORD ScriptFlags, // = 0
BOOL bDoAdvertise, // = TRUE
BOOL bAddAppData // = TRUE
) { HKEY hkApp; WCHAR wszDeploymentId[40]; DWORD AppState; DWORD Status; BOOL bAdvertised; BOOL bUnmanageUninstall;
//
// For async refreshes, We do not want to perform any assignments that are destined to
// lead to installs since that would disrupt the user. However, pure advertisements
// are ok -- they cause minimal disruption and no loss of user functionality
//
if ( _pManApp->Async() ) { if ( ( ACTION_INSTALL == _Action ) || ( ACTION_REINSTALL == _Action ) ) { DebugMsg((DM_VERBOSE, IDS_ABORT_OPERATION));
_Status = ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED; Status = _Status;
goto ReportStatus; } }
Status = ERROR_SUCCESS;
bAdvertised = FALSE; bUnmanageUninstall = FALSE;
GuidToString( _DeploymentId, wszDeploymentId );
if ( ! bDoAdvertise ) goto SaveAppData;
DebugMsg((DM_VERBOSE, IDS_ASSIGN, _pwszDeploymentName, _pwszGPOName));
Status = CopyScriptIfNeeded();
if ( (Status != ERROR_SUCCESS) && bDoAdvertise && bAddAppData ) { gpEvents->Assign( Status, this ); return Status; }
//
// Do this before determining script flags because an uninstall will cause
// the app state to change.
//
Status = EnforceAssignmentSecurity( &bUnmanageUninstall );
//
// Note that we check the error status for the call above below -- we
// do not overwrite the status if it is an error. Better code arrangement
// is called for here, but we are making the smallest change since an
// issue with this was found at the end of xpsp1
//
if ( 0 == ScriptFlags ) { //
// Apps which are installed on demand (ARP, shell/com activation) only
// have the Darwin product registry keys advertised.
// This is all we need to make the MsiConfigureProduct call in the
// client work and ensure that if the install must be rolled back, we
// get back to the true previous state of the machine.
//
// The icons/transforms have to be included in any first time advertise
// because of some bizarre design in Darwin. If we don't advertise
// it then the app's shorcuts will not have icons even after the install.
// For subsequent advertises we don't include the icons/transforms because
// this is a very expensive part of advertisement because it extracts and
// re-creates icons from the script file. [Removed 12/23/98, we'll see
// if Darwin handles this correctly now].
//
// During policy runs we advertise shortcuts for previously advertised
// assigned apps.
//
// For published apps which we're applying for the first time on a machine
// we just advertise the config data. Note that the only time a published
// app is advertised as part of a policy run is for the roaming profile
// scenario where the app has never been applied to the machine we're now on.
// We don't want to advertise shorcuts nor class data in this case because
// only a subset of the product's features may have been installed on the
// original machine. We want to preserve this feature state.
//
// The very first time an assigned app is assigned to a machine we will also
// advertise the shell class data. But since this is quite heavy
// weight, we only do it the first time. If it is ever lost somehow,
// a miss on this info will result in a DS query and install anyway.
//
ScriptFlags = SCRIPTFLAGS_REGDATA_CNFGINFO;
if ( (_State & APPSTATE_SCRIPT_NOT_EXISTED) || ! AppPresent(_InstallState) ) { ScriptFlags |= SCRIPTFLAGS_CACHEINFO;
//
// Now we decide whether or not to validate the transform list
//
// MsiAdvertiseScript incorrectly detects transform conflicts between a user
// and machine installed version of the same application, so we
// only validate transforms for user policy if the application is already
// in a non absent state per-user. A transform conflict cannot
// be possible in that case despite what MsiAdvertiseScript claims.
//
// We check for the application's presence below -- note that we can't use
// the _InstallState member since it is not guaranteed to be set when
// we reach this point.
//
// Note : all this code was also added for a win2k-sp3, and xpsp1 fix.
//
BOOL bValidateTransforms = TRUE;
if ( _pManApp->IsUserPolicy() ) { INSTALLSTATE InstallState = (*gpfnMsiQueryProductState)( _pwszProductId ); WCHAR wszBuffer[8]; DWORD Size = sizeof(wszBuffer) / sizeof(WCHAR);
if ( AppPresent(InstallState) ) { //
// Be sure to use a new error status here so we don't
// overwrite the existing status which should be checked later
//
DWORD ProductInfoStatus;
ProductInfoStatus = (*gpfnMsiGetProductInfo)( _pwszProductId, INSTALLPROPERTY_ASSIGNMENTTYPE, wszBuffer, &Size ); //
// Only if we don't already have a failure status should we overwrite
// the existing status -- otherwise, we will miss the failure in the
// call to EnforceAssignmentSecurity, which can happen when we need
// to uninstall an unmanaged app but we're in async refresh. This issue
// was found at the end of xpsp1 and was caused when this new code was
// added to properly detect transform conflicts in xpsp1. We are making
// the smallest change here to restore the failure path to what it was
// before the transform fix for xpsp1 was made.
//
if ( ERROR_SUCCESS == Status ) { Status = ProductInfoStatus; }
// '1' means installed per-machine
if ( (ERROR_SUCCESS == ProductInfoStatus) && (L'1' == wszBuffer[0]) ) bValidateTransforms = FALSE; } }
//
// The application is present, so we must tell Msi to guard against transform
// conflicts between the existing application and the one we are trying to install
//
if ( ! (_State & APPSTATE_TRANSFORM_CONFLICT) && bValidateTransforms ) ScriptFlags |= SCRIPTFLAGS_VALIDATE_TRANSFORMS_LIST; }
//
// When an unmanaged instance of the product is uninstall we need to ensure
// that shortcuts are always added back for the managed product.
//
if ( bUnmanageUninstall ) ScriptFlags |= SCRIPTFLAGS_SHORTCUTS;
if ( ( _State & APPSTATE_FULL_ADVERTISE ) || _bRestored ) { //
// This part here means we are in a policy run in winlogon, not
// in the service doing a demand install. We are advertising
// an assigned app, a published app which is an upgrade,
// or a published app which was removed on another logon (possibly
// another machine) because its gpo went out of scope but has
// now come back into scope.
//
ScriptFlags |= SCRIPTFLAGS_SHORTCUTS;
//
// The first time we apply an assigned app on a machine, we do a
// full advertise. Otherwise, we don't advertise shell class
// registry data and icon/transform data.
//
if ( (_State & APPSTATE_SCRIPT_NOT_EXISTED) || ! AppPresent(_InstallState) ) ScriptFlags |= SCRIPTFLAGS_REGDATA_EXTENSIONINFO; } }
if ( ! _pManApp->IsUserPolicy() ) ScriptFlags |= SCRIPTFLAGS_MACHINEASSIGN;
if ( ERROR_SUCCESS == Status ) { DebugMsg((DM_VERBOSE, IDS_ADVERTISE, _pwszDeploymentName, _pwszLocalScriptPath, ScriptFlags));
Status = CallMsiAdvertiseScript( _pwszLocalScriptPath, ScriptFlags, NULL, FALSE ); }
if ( ERROR_SUCCESS == Status ) { bAdvertised = TRUE;
if ( _State & APPSTATE_SCRIPT_NOT_EXISTED ) _AssignCount++; } else { DebugMsg((DM_WARNING, IDS_ADVERTISE_FAIL, _pwszDeploymentName, _pwszLocalScriptPath, Status)); }
//
// Abort early in this case without deleting any management data.
// The app will be reapplied asynchronously in this case.
//
if ( (ACTION_APPLY == _Action) && (ERROR_INSTALL_ALREADY_RUNNING == Status) ) return Status;
//
// If we have a transform conflict with this app, then abort the assign
// now without removing the app. Callers of this routine will fix up
// the app state and retry if appropriate.
//
if ( ERROR_INSTALL_TRANSFORM_FAILURE == Status ) return Status;
SaveAppData:
//
// Always set this, even when bAddAppData is FALSE. This controls the
// UI level used for descriptor based installs from shell & com. The
// last writer (highest precedence app for a product id) wins.
//
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( _pManApp->AppmgmtKey(), _pwszProductId, 0, REG_DWORD, (LPBYTE) &_InstallUILevel, sizeof(DWORD) ); }
//
// We only write/update our registry state if we've gotten info back down
// from the Directory.
//
if ( (ERROR_SUCCESS == Status) && bAddAppData && (_ActFlags != 0) ) { _pManApp->Revert();
hkApp = 0;
Status = RegCreateKeyEx( _pManApp->AppmgmtKey(), wszDeploymentId, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hkApp, NULL );
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, DEPLOYMENTNAMEVALUE, 0, REG_SZ, (LPBYTE) _pwszDeploymentName, lstrlen( _pwszDeploymentName ) * sizeof(WCHAR) + sizeof(WCHAR) ); }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, GPONAMEVALUE, 0, REG_SZ, (LPBYTE) _pwszGPOName, lstrlen( _pwszGPOName ) * sizeof(WCHAR) + sizeof(WCHAR) ); }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, GPOIDVALUE, 0, REG_SZ, (LPBYTE) _pwszGPOId, lstrlen( _pwszGPOId ) * sizeof(WCHAR) + sizeof(WCHAR) ); }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, PRODUCTIDVALUE, 0, REG_SZ, (LPBYTE) _pwszProductId, lstrlen( _pwszProductId ) * sizeof(WCHAR) + sizeof(WCHAR) ); }
if ( _pwszSupportURL && (ERROR_SUCCESS == Status) ) { Status = RegSetValueEx( hkApp, SUPPORTURL, 0, REG_SZ, (LPBYTE) _pwszSupportURL, lstrlen( _pwszSupportURL ) * sizeof(WCHAR) + sizeof(WCHAR) ); }
if ( (ERROR_SUCCESS == Status) && (_Overrides > 0) ) { WCHAR * pwszSupercededIds; WCHAR * pwszString;
pwszSupercededIds = new WCHAR[(_Overrides * (GUIDSTRLEN + 1)) + 1];
if ( pwszSupercededIds ) { pwszString = pwszSupercededIds; for ( DWORD n = 0; n < _Overrides; n++ ) { GuidToString( _pOverrides[n], pwszString ); pwszString += GUIDSTRLEN + 1; } *pwszString = 0;
Status = RegSetValueEx( hkApp, SUPERCEDEDIDS, 0, REG_MULTI_SZ, (LPBYTE) pwszSupercededIds, (_Overrides * (GUIDSTRLEN + 1) + 1) * sizeof(WCHAR) );
delete [] pwszSupercededIds; } else { Status = ERROR_OUTOFMEMORY; } }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, ASSIGNCOUNTVALUE, 0, REG_DWORD, (LPBYTE) &_AssignCount, sizeof(DWORD) ); }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, REVISIONVALUE, 0, REG_DWORD, (LPBYTE) &_DirectoryRevision, sizeof(DWORD) );
if ( (ERROR_SUCCESS == Status) && (_DirectoryRevision > 0) ) { Status = RegSetValueEx( hkApp, SCRIPTTIMEVALUE, 0, REG_BINARY, (LPBYTE) &_ScriptTime, sizeof(_ScriptTime) ); } }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, INSTALLUI, 0, REG_DWORD, (LPBYTE) &_InstallUILevel, sizeof(DWORD) ); }
if ( ERROR_SUCCESS == Status ) { if ( _ActFlags ) { AppState = 0;
//
// If an app becomes disabled then it gets neither the assigned
// nor the published state bit.
//
if ( _ActFlags & ACTFLG_Assigned ) AppState |= APPSTATE_ASSIGNED; else if ( _ActFlags & ACTFLG_Published ) AppState |= APPSTATE_PUBLISHED;
AppState |= (_ActFlags & ACTFLG_UninstallOnPolicyRemoval) ? APPSTATE_POLICYREMOVE_UNINSTALL : APPSTATE_POLICYREMOVE_ORPHAN; if ( _bNeedsUnmanagedRemove ) AppState |= APPSTATE_UNINSTALL_UNMANAGED; if ( _ActFlags & ACTFLG_InstallUserAssign ) AppState |= APPSTATE_INSTALL; } else { AppState = _State; }
AppState &= APPSTATE_PERSIST_MASK;
Status = RegSetValueEx( hkApp, APPSTATEVALUE, 0, REG_DWORD, (LPBYTE) &AppState, sizeof(DWORD) ); }
if ( hkApp ) RegCloseKey( hkApp );
DWORD impStatus = _pManApp->Impersonate(); if ( (impStatus != ERROR_SUCCESS) && (Status == ERROR_SUCCESS) ) { Status = impStatus; } }
ReportStatus:
if ( bDoAdvertise && bAddAppData ) gpEvents->Assign( Status, this );
//
// If we hit an error then the destructor of this object will make sure we delete the script file so a full
// advertise will be tried next time.
//
return Status; }
DWORD CAppInfo::Install() { DWORD Status;
//
// During async refreshes, we will not install an application as that
// would be disruptive to the user
//
if ( ! _pManApp->Async() ) { //
// Installs can happen for machine assign apps or for user apps when there
// are transform conflicts.
//
// Always set UI to NONE.
//
(*gpfnMsiSetInternalUI)( INSTALLUILEVEL_NONE, NULL );
DebugMsg((DM_VERBOSE, IDS_INSTALL, _pwszDeploymentName, _pwszGPOName));
Status = CallMsiConfigureProduct( _pwszProductId, INSTALLLEVEL_DEFAULT, INSTALLSTATE_DEFAULT, NULL ); } else { DebugMsg((DM_VERBOSE, IDS_ABORT_OPERATION));
_Status = ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED; Status = _Status; }
gpEvents->Install( Status, this );
return Status; }
DWORD CAppInfo::Reinstall() { DWORD Status;
//
// For async refreshes, We do not want to reinstall an application
// since that would disrupt the user
//
if ( ! _pManApp->Async() ) { //
// Reinstalls can happen when a redeploy action is specified in the SI UI.
// This is normally done when a binary patch is applied to an application.
//
// Always set UI to NONE.
//
(*gpfnMsiSetInternalUI)( INSTALLUILEVEL_NONE, NULL );
DebugMsg((DM_VERBOSE, IDS_REINSTALL, _pwszDeploymentName));
Status = CallMsiReinstallProduct( _pwszProductId ); } else { DebugMsg((DM_VERBOSE, IDS_ABORT_OPERATION));
_Status = ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED; Status = _Status; }
gpEvents->Reinstall( Status, this );
return Status; }
DWORD CAppInfo::Unassign( DWORD ScriptFlags, // = 0
BOOL bRemoveAppData // = TRUE
) { HKEY hkApp; WCHAR wszDeploymentId[40]; DWORD AppState; DWORD Status;
ASSERT( ! _pManApp->GetRsopContext()->IsPlanningModeEnabled() );
//
// For async refreshes, We do not want to unassign an application
// since that would disrupt the user
//
if ( _pManApp->Async() ) { DebugMsg((DM_VERBOSE, IDS_ABORT_OPERATION));
_Status = ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED;
if ( bRemoveAppData ) gpEvents->Unassign( _Status, this ); return _Status; }
GuidToString( _DeploymentId, wszDeploymentId);
if ( bRemoveAppData ) DebugMsg((DM_VERBOSE, IDS_UNMANAGE, _pwszDeploymentName));
Status = STATUS_SUCCESS;
if ( ScriptFlags != 0 ) { if ( ! _pManApp->IsUserPolicy() ) ScriptFlags |= SCRIPTFLAGS_MACHINEASSIGN;
DebugMsg((DM_VERBOSE, IDS_UNADVERTISE, _pwszDeploymentName, _pwszLocalScriptPath));
Status = CallMsiAdvertiseScript( _pwszLocalScriptPath, ScriptFlags, NULL, TRUE );
// It's possible for the product to be uninstalled without us knowing.
if ( ERROR_UNKNOWN_PRODUCT == Status ) Status = ERROR_SUCCESS;
if ( Status != ERROR_SUCCESS ) DebugMsg((DM_WARNING, IDS_UNADVERTISE_FAIL, _pwszDeploymentName, _pwszLocalScriptPath, Status)); }
if ( (ERROR_SUCCESS == Status) && bRemoveAppData ) { _pManApp->Revert();
DeleteFile( _pwszLocalScriptPath ); _State &= ~APPSTATE_SCRIPT_PRESENT;
//
// Do not delete the productid->installui hint value when a product is removed
// because of productid or transform conflicts. In both of these cases it means
// an app with the same productid is still currently being managed.
// This is important because the unmanage calls are done last when handling ARP
// requests and this will inadvertently delete the productid->installui hint value
// for a product which is still being managed.
//
if ( (_dwRemovalCause != APP_ATTRIBUTE_REMOVALCAUSE_PRODUCT) && (_dwRemovalCause != APP_ATTRIBUTE_REMOVALCAUSE_TRANSFORM) ) RegDeleteValue( _pManApp->AppmgmtKey(), _pwszProductId );
_AssignCount--;
if ( _AssignCount > 0 ) { BOOL bUpdateState; bUpdateState = TRUE;
if ( ACTION_ORPHAN == _Action ) { DebugMsg((DM_VERBOSE, IDS_UNMANAGE_ORPHAN, _pwszDeploymentName)); AppState = APPSTATE_ORPHANED; } else if ( ( ACTION_UNINSTALL == _Action ) || ( ACTION_NONE == _Action ) ) { DebugMsg((DM_VERBOSE, IDS_UNMANAGE_UNINSTALL, _pwszDeploymentName)); AppState = APPSTATE_UNINSTALLED; } else { bUpdateState = FALSE; }
Status = RegOpenKeyEx( _pManApp->AppmgmtKey(), wszDeploymentId, 0, KEY_ALL_ACCESS, &hkApp );
if ( ERROR_SUCCESS == Status ) { DWORD RemovedState;
RemovedState = _State & APPSTATE_PERSIST_MASK;
if ( _pManApp->IsRemovingPolicies() ) { Status = RegSetValueEx( hkApp, REMOVEDGPOSTATE, 0, REG_DWORD, (LPBYTE) &RemovedState, sizeof(DWORD) ); } if ( bUpdateState && ( ERROR_SUCCESS == Status ) ) { Status = RegSetValueEx( hkApp, APPSTATEVALUE, 0, REG_DWORD, (LPBYTE) &AppState, sizeof(DWORD) ); }
if ( ERROR_SUCCESS == Status ) { Status = RegSetValueEx( hkApp, ASSIGNCOUNTVALUE, 0, REG_DWORD, (LPBYTE) &_AssignCount, sizeof(DWORD) ); }
RegCloseKey( hkApp ); }
if ( Status != ERROR_SUCCESS ) { Status = ERROR_SUCCESS; RegDeleteKey( _pManApp->AppmgmtKey(), wszDeploymentId ); } } else { RegDeleteKey( _pManApp->AppmgmtKey(), wszDeploymentId ); }
DWORD impStatus = _pManApp->Impersonate(); if ( (impStatus != ERROR_SUCCESS) && (Status == ERROR_SUCCESS) ) { Status = impStatus; } }
if ( bRemoveAppData ) gpEvents->Unassign( Status, this );
return Status; }
DWORD CAppInfo::Uninstall( BOOL bLogFailure // = TRUE
) { DWORD Status;
//
// For async refreshes, We do not want to uninstall an application
// since that would disrupt the user
//
if ( ! _pManApp->Async() ) { // Uninstalls happen in our background thread. Always set UI to NONE.
(*gpfnMsiSetInternalUI)( INSTALLUILEVEL_NONE, NULL );
DebugMsg((DM_VERBOSE, IDS_UNINSTALL, _pwszDeploymentName, _pwszGPOName));
Status = CallMsiConfigureProduct( _pwszProductId, INSTALLLEVEL_MAXIMUM, INSTALLSTATE_ABSENT, NULL );
// It's possible for the product to be uninstalled without us knowing.
if ( ERROR_UNKNOWN_PRODUCT == Status ) Status = ERROR_SUCCESS; } else { DebugMsg((DM_VERBOSE, IDS_ABORT_OPERATION));
_Status = ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED; Status = _Status; }
if ( bLogFailure ) { gpEvents->Uninstall( Status, this ); }
return Status; }
HRESULT CAppInfo::Write( CPolicyRecord* pRecord ) { HRESULT hr; LONG EntryType; WCHAR wszDeploymentId[ MAX_SZGUID_LEN ];
EntryType = GetPublicRsopEntryType();
hr = pRecord->SetValue( APP_ATTRIBUTE_ENTRYTYPE, EntryType);
if (FAILED(hr)) { goto cleanup; }
hr = pRecord->SetValue( RSOP_ATTRIBUTE_NAME, _pwszDeploymentName);
REPORT_ATTRIBUTE_SET_STATUS( RSOP_ATTRIBUTE_NAME, hr )
if (FAILED(hr)) { goto cleanup; }
GuidToString( _DeploymentId, wszDeploymentId);
hr = pRecord->SetValue( APP_ATTRIBUTE_APPID, wszDeploymentId);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_APPID, hr );
if (FAILED(hr)) { goto cleanup; }
hr = pRecord->SetValue( APP_ATTRIBUTE_ENTRYTYPE, EntryType); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ENTRYTYPE, hr )
if (FAILED(hr)) { goto cleanup; }
//
// For the remaining attributes, we will ignore failures -- at this
// point, they have the application name, which is useful in
// and of itself.
//
//
// These properties are only written for new records -- if we are creating
// an instance from an existing record, will not write these properties since
// the current instance already has them set correctly and the reason that we're
// re-using this instance is that we do not have this information
//
if ( pRecord->AlreadyExists() ) { return hr; } { SYSTEMTIME CurrentTime;
//
// This does not fail
//
GetSystemTime( &CurrentTime );
hr = pRecord->SetValue( RSOP_ATTRIBUTE_CREATIONTIME, &CurrentTime);
REPORT_ATTRIBUTE_SET_STATUS( RSOP_ATTRIBUTE_CREATIONTIME, hr ); }
hr = pRecord->SetValue( RSOP_ATTRIBUTE_GPOID, _pwszGPODSPath);
REPORT_ATTRIBUTE_SET_STATUS( RSOP_ATTRIBUTE_GPOID, hr )
hr = pRecord->SetValue( RSOP_ATTRIBUTE_SOMID, _pwszSOMId);
REPORT_ATTRIBUTE_SET_STATUS( RSOP_ATTRIBUTE_SOMID, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_ENTRYTYPE, EntryType);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ENTRYTYPE, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_SECURITY_DESCRIPTOR, _rgSecurityDescriptor, _cbSecurityDescriptor);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_SECURITY_DESCRIPTOR, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_VERSIONLO, (LONG)_VersionLo);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_VERSIONLO, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_VERSIONHI, (LONG)_VersionHi);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_VERSIONHI, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_PRODUCT_ID, _pwszProductId);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_PRODUCT_ID, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_SCRIPTFILE, _pwszGPTScriptPath);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_SCRIPTFILE, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_LANGUAGEID, (LONG) _LangId);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_LANGUAGEID, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_DEPLOY_TYPE, (_ActFlags & ACTFLG_Assigned ? APP_ATTRIBUTE_DEPLOY_VALUE_ASSIGNED : APP_ATTRIBUTE_DEPLOY_VALUE_PUBLISHED));
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_DEPLOY_TYPE, hr )
{ LONG AssignmentType; if ( _ActFlags & ACTFLG_Published ) { AssignmentType = APP_ATTRIBUTE_ASSIGNMENTTYPE_VALUE_NOTASSIGNED; } else if ( _ActFlags & ACTFLG_InstallUserAssign ) { AssignmentType = APP_ATTRIBUTE_ASSIGNMENTTYPE_VALUE_INSTALL; } else { AssignmentType = APP_ATTRIBUTE_ASSIGNMENTTYPE_VALUE_STANDARD; } hr = pRecord->SetValue( APP_ATTRIBUTE_ASSIGNMENT_TYPE, AssignmentType);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ASSIGNMENT_TYPE, hr ) }
hr = pRecord->SetValue( APP_ATTRIBUTE_INSTALLATIONUI, ((INSTALLUILEVEL_FULL == _InstallUILevel) ? APP_ATTRIBUTE_INSTALLATIONUI_VALUE_MAXIMUM : APP_ATTRIBUTE_INSTALLATIONUI_VALUE_BASIC));
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_INSTALLATIONUI, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_ONDEMAND, (BOOL)(_ActFlags & ACTFLG_OnDemandInstall));
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_LOSSOFSCOPEACTION, (BOOL)(_ActFlags & ACTFLG_OrphanOnPolicyRemoval) ? APP_ATTRIBUTE_SCOPELOSS_ORPHAN : APP_ATTRIBUTE_SCOPELOSS_UNINSTALL);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_LOSSOFSCOPEACTION, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_IGNORELANGUAGE, (BOOL)(_ActFlags & ACTFLG_IgnoreLanguage));
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_IGNORELANGUAGE, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_PACKAGELOCATION, _pwszPackageLocation);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_PACKAGELOCATION, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_CATEGORYLIST, _rgwszCategories, _cCategories);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_CATEGORYLIST, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_TRANSFORMLIST, _rgwszTransforms, _cTransforms);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_TRANSFORMLIST, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_ARCHITECTURES, _rgArchitectures, _cArchitectures);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ARCHITECTURES, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_PUBLISHER, _pwszPublisher ? _pwszPublisher : L"" );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_PUBLISHER, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_REDEPLOYCOUNT, (LONG) _DirectoryRevision);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REDEPLOYCOUNT, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_UPGRADE_SETTINGS_MANDATORY, (BOOL) ( _ActFlags & ACTFLG_ForceUpgrade ) );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_UPGRADE_SETTINGS_MANDATORY, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_UNINSTALL_UNMANAGED, (BOOL) ( _bNeedsUnmanagedRemove ) );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_UNINSTALL_UNMANAGED, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_DISPLAYINARP, (BOOL) ( _ActFlags & ACTFLG_UserInstall ) );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_DISPLAYINARP, hr );
hr = pRecord->SetValue( APP_ATTRIBUTE_SUPPORTURL, _pwszSupportURL);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_SUPPORTURL, hr );
if ( APP_ATTRIBUTE_ENTRYTYPE_VALUE_REMOVED_PACKAGE == EntryType ) { hr = WriteRemovalProperties( pRecord ); }
{ BOOL bX86OnWin64;
bX86OnWin64 = FALSE;
//
// If this is an x86 package, see if this applies to 64-bit clients
//
if ( PROCESSOR_ARCHITECTURE_INTEL == _PrimaryArchitecture ) { //
// If it has been excluded by the admin, it does not apply
//
if ( ! ( ACTFLG_ExcludeX86OnWin64 & _ActFlags ) ) { bX86OnWin64 = TRUE; } //
// The flag above is reversed if this is a ZAP app -- flip
// the logic to support the reverse preference.
//
if ( SetupNamePath == _PathType ) { bX86OnWin64 = ! bX86OnWin64; } }
hr = pRecord->SetValue( APP_ATTRIBUTE_X86OnWin64, bX86OnWin64);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_X86OnWin64, hr); }
{ SYSTEMTIME SystemTime; BOOL bStatus;
bStatus = FileTimeToSystemTime( (FILETIME*) &_USN, &SystemTime);
if ( bStatus ) { hr = pRecord->SetValue( APP_ATTRIBUTE_MODIFYTIME, &SystemTime);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_MODIFYTIME, hr); } else { hr = HRESULT_FROM_WIN32( GetLastError() ); } }
{ LONG PackageType;
switch ( _PathType ) { case DrwFilePath: PackageType = APP_ATTRIBUTE_PACKAGETYPE_VALUE_WIN_INSTALLER; break;
case SetupNamePath: PackageType = APP_ATTRIBUTE_PACKAGETYPE_VALUE_ZAP; break;
default: ASSERT ( L"Invalid packagetype" && FALSE ); break; }
hr = pRecord->SetValue( APP_ATTRIBUTE_PACKAGETYPE, (LONG) PackageType);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_PACKAGETYPE, hr ) }
if ( ! _pManApp->ARPList() ) { DWORD dwInstallType; WCHAR* wszDemandSpec; WCHAR* wszProperty;
dwInstallType = _pManApp->GetRsopContext()->GetDemandSpec( &wszDemandSpec ); wszProperty = NULL;
switch (dwInstallType) { case CRsopAppContext::DEMAND_INSTALL_FILEEXT:
wszProperty = APP_ATTRIBUTE_ONDEMAND_FILEEXT; _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_FILEEXT; break;
case CRsopAppContext::DEMAND_INSTALL_CLSID:
wszProperty = APP_ATTRIBUTE_ONDEMAND_CLSID; _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_CLSID; break;
case CRsopAppContext::DEMAND_INSTALL_PROGID:
wszProperty = APP_ATTRIBUTE_ONDEMAND_PROGID; _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_PROGID; break;
case CRsopAppContext::DEMAND_INSTALL_NAME:
_dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_USER; wszProperty = NULL; break;
case CRsopAppContext::DEMAND_INSTALL_NONE:
//
// We reach this case if we are in policy application
//
wszProperty = NULL;
if ( ( _dwApplyCause == APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE ) && ! IsSuperseded() ) { if ( _ActFlags & ACTFLG_Assigned ) { _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED; } else if ( _ActFlags & ACTFLG_Published ) { if ( _State & APPSTATE_SCRIPT_NOT_EXISTED ) { _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_PROFILE; } else if ( _State & APPSTATE_ASSIGNED ) { _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_ASSIGNED; } else { _dwApplyCause = APP_ATTRIBUTE_APPLYCAUSE_VALUE_USER;
if ( ! _pManApp->GetRsopContext()->Transition() ) { _dwApplyCause = _dwUserApplyCause; wszProperty = _wszDemandProp; wszDemandSpec = _wszDemandSpec; } } } }
break;
default: ASSERT( L"Invalid RSoP Install Context" && FALSE ); break;
}
if ( wszProperty ) { hr = pRecord->SetValue( wszProperty, wszDemandSpec);
REPORT_ATTRIBUTE_SET_STATUS( wszProperty, hr ) } else if ( APP_ATTRIBUTE_ENTRYTYPE_VALUE_REMOVED_PACKAGE != EntryType ) { //
// Ensure that
//
hr = pRecord->ClearValue( APP_ATTRIBUTE_ONDEMAND_FILEEXT);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND_FILEEXT, hr )
hr = pRecord->ClearValue( APP_ATTRIBUTE_ONDEMAND_CLSID);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND_CLSID, hr )
hr = pRecord->ClearValue( APP_ATTRIBUTE_ONDEMAND_PROGID);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ONDEMAND_PROGID, hr ) } if ( APP_ATTRIBUTE_APPLYCAUSE_VALUE_NONE != _dwApplyCause ) { hr = pRecord->SetValue( APP_ATTRIBUTE_APPLY_CAUSE, (LONG) _dwApplyCause); REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_APPLY_CAUSE, hr ) } }
{ LONG MatchType;
switch(_LanguageWeight) { case PRI_LANG_ALWAYSMATCH: MatchType = APP_ATTRIBUTE_LANGMATCH_VALUE_IGNORE; break;
case PRI_LANG_SYSTEMLOCALE: MatchType = APP_ATTRIBUTE_LANGMATCH_VALUE_SYSLOCALE; break;
case PRI_LANG_ENGLISH: MatchType = APP_ATTRIBUTE_LANGMATCH_VALUE_ENGLISH; break;
case PRI_LANG_NEUTRAL: MatchType = APP_ATTRIBUTE_LANGMATCH_VALUE_NEUTRAL; break;
default: MatchType = APP_ATTRIBUTE_LANGMATCH_VALUE_NOMATCH; break; }
hr = pRecord->SetValue( APP_ATTRIBUTE_LANGMATCH, MatchType);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_LANGMATCH, hr ) }
hr = pRecord->SetValue( APP_ATTRIBUTE_ELIGIBILITY, GetEligibility() );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_ELIGIBILITY, hr )
LogUpgrades( pRecord );
cleanup:
return hr; }
HRESULT CAppInfo::WriteRemovalProperties( CPolicyRecord* pRemovalRecord ) { HRESULT hr; LONG RemovalType; LONG RemovalCause;
RemovalCause = (LONG) _dwRemovalCause;
if ( CRsopAppContext::REMOVAL == _pManApp->GetRsopContext()->GetContext() ) { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_UNINSTALLED; } else if ( APP_ATTRIBUTE_REMOVALCAUSE_UPGRADE == RemovalCause ) { if ( ACTION_UNINSTALL == Action() ) { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_UNINSTALLED; } else { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_UPGRADED; } } else if ( APP_ATTRIBUTE_REMOVALCAUSE_TRANSFORM == RemovalCause ) { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_UNINSTALLED; } else if ( ACTION_ORPHAN == Action() ) { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_ORPHAN; } else if ( ACTION_UNINSTALL == Action() ) { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_UNINSTALLED; } else { RemovalType = APP_ATTRIBUTE_REMOVALTYPE_NONE; }
if ( APP_ATTRIBUTE_REMOVALTYPE_NONE == RemovalType ) { return S_OK; } hr = pRemovalRecord->SetValue( APP_ATTRIBUTE_REMOVAL_CAUSE, RemovalCause);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REMOVAL_CAUSE, hr );
hr = pRemovalRecord->SetValue( APP_ATTRIBUTE_REMOVAL_TYPE, RemovalType);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REMOVAL_TYPE, hr );
hr = pRemovalRecord->ClearValue( APP_ATTRIBUTE_PRECEDENCE_REASON);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_PRECEDENCE_REASON, hr );
if ( _pwszRemovingDeploymentId ) { hr = pRemovalRecord->SetValue( APP_ATTRIBUTE_REMOVING_APP, _pwszRemovingDeploymentId);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REMOVING_APP, hr ) }
return hr; }
HRESULT CAppInfo::ClearRemovalProperties( CPolicyRecord* pRecord ) { HRESULT hr;
hr = pRecord->ClearValue( APP_ATTRIBUTE_REMOVAL_CAUSE );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REMOVAL_CAUSE, hr )
if ( SUCCEEDED( hr ) ) { hr = pRecord->ClearValue( APP_ATTRIBUTE_REMOVAL_TYPE );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REMOVAL_TYPE, hr ) }
if ( SUCCEEDED( hr ) ) { hr = pRecord->ClearValue( APP_ATTRIBUTE_REMOVING_APP );
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REMOVING_APP, hr ) }
return hr; }
LONG CAppInfo::GetRsopEntryType() { LONG EntryType;
if ( _pManApp->ARPList() ) { EntryType = APP_ATTRIBUTE_ENTRYTYPE_VALUE_ARPLIST_ITEM; } else { if ( ( _Action == ACTION_APPLY ) || ( _Action == ACTION_INSTALL ) || ( _Action == ACTION_REINSTALL ) ) { EntryType = APP_ATTRIBUTE_ENTRYTYPE_VALUE_INSTALLED_PACKAGE; } else if ( ( _Action == ACTION_ORPHAN ) || ( _Action == ACTION_UNINSTALL ) ) { EntryType = APP_ATTRIBUTE_ENTRYTYPE_VALUE_REMOVED_PACKAGE; } else if ( ( _State & APPSTATE_ASSIGNED ) | ( _State & APPSTATE_PUBLISHED ) ) { EntryType = APP_ATTRIBUTE_ENTRYTYPE_VALUE_INSTALLED_PACKAGE; } else { EntryType = NO_RSOP_ENTRY; } }
return EntryType; }
LONG CAppInfo::GetPublicRsopEntryType() { LONG EntryType;
if ( IsSuperseded() ) { if ( _pManApp->ARPList() ) { EntryType = APP_ATTRIBUTE_ENTRYTYPE_VALUE_ARPLIST_ITEM; } else { EntryType = APP_ATTRIBUTE_ENTRYTYPE_VALUE_INSTALLED_PACKAGE; } } else { EntryType = GetRsopEntryType(); }
return EntryType; }
LONG CAppInfo::GetEligibility() { LONG Eligibility;
Eligibility = 0;
if ( ! ( CRsopAppContext::ARPLIST == _pManApp->GetRsopContext()->GetContext() ) ) { if ( ! ( CRsopAppContext::POLICY_REFRESH == _pManApp->GetRsopContext()->GetContext() ) && ! IsSuperseded() ) { Eligibility = APP_ATTRIBUTE_ELIGIBILITY_VALUE_APPLIED; } else if ( ( _ActFlags & ACTFLG_Assigned ) || ( _State & APPSTATE_ASSIGNED ) ) { Eligibility = APP_ATTRIBUTE_ELIGIBILITY_VALUE_ASSIGNED; } else if ( _ActFlags & ACTFLG_HasUpgrades ) { Eligibility = APP_ATTRIBUTE_ELIGIBILITY_VALUE_UPGRADES; } else if ( ( _dwApplyCause == APP_ATTRIBUTE_APPLYCAUSE_VALUE_PROFILE ) || _DemandInstall ) { Eligibility = APP_ATTRIBUTE_ELIGIBILITY_VALUE_APPLIED; } else if ( _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { Eligibility = APP_ATTRIBUTE_ELIGIBILITY_VALUE_PLANNING; } else { Eligibility = APP_ATTRIBUTE_ELIGIBILITY_VALUE_APPLIED; } }
return Eligibility; }
HRESULT CAppInfo::SetRemovingDeploymentId( GUID* pDeploymentId ) { if ( ! _pwszRemovingDeploymentId ) { _pwszRemovingDeploymentId = new WCHAR[ MAX_SZGUID_LEN ]; }
if ( ! _pwszRemovingDeploymentId ) { return E_OUTOFMEMORY; }
GuidToString( *pDeploymentId, _pwszRemovingDeploymentId);
return S_OK; }
void CAppInfo::SetRsopFailureStatus( DWORD dwStatus, DWORD dwEventId) { CAppStatus* pNewStatus;
//
// Allocate a new failure status for this application --
// it will be freed by the destructor of this class
//
pNewStatus = new CAppStatus;
if ( ! pNewStatus ) { _pManApp->GetRsopContext()->DisableRsop( E_OUTOFMEMORY ); return; }
//
// Set the status as specified by the caller
//
pNewStatus->SetRsopFailureStatus( dwStatus, dwEventId );
//
// Remember this new status --
// keep track of multiple errors in the order they were logged
//
_StatusList.InsertFIFO( pNewStatus );
//
// Always reset this so that if someone calls GetCurrentItem,
// they will get the first thing in the list -- if this is
// never called, the list returns NULL
//
_StatusList.Reset(); }
void CAppInfo::ForceFailureStatus() { //
// If we haven't already failed, set an arbitrary
// error code -- this is used in cases where the app
// itself did not fail to apply, but some other app
// failed (as in the upgrade case), but we need to
// set this as a failure so that the application will
// clean up its state
//
if ( ERROR_SUCCESS == _Status ) { _Status = ERROR_NOT_ENOUGH_MEMORY; } }
LONG CAppInfo::UpdatePrecedence( CAppInfo* pLosingApp, DWORD dwConflict ) { LONG Status;
Status = ERROR_SUCCESS;
if ( ! _bSupersedesAssigned ) { if ( pLosingApp->_bSupersedesAssigned ) { _bSupersedesAssigned = pLosingApp->_bSupersedesAssigned; } else { _bSupersedesAssigned = ( ACTFLG_Assigned & pLosingApp->_ActFlags ); } }
//
// If Rsop logging is enabled, we need to update
// the precedence of this application according
// to the conflict
//
if ( _pManApp->GetRsopContext()->IsRsopEnabled() ) { Status = _SupersededApps.AddConflict( pLosingApp, this, dwConflict );
//
// Supersede the losing app -- that is, mark it as
// superseded so that later on when writing the rsop
// log we do not log it as a winning application
//
if ( ERROR_SUCCESS == Status ) { pLosingApp->Supersede();
DebugMsg(( DM_VERBOSE, IDS_RSOP_SUPERSEDED, pLosingApp->_pwszDeploymentName, pLosingApp->_pwszGPOName, _pwszDeploymentName, _pwszGPOName, dwConflict)); } else { HRESULT hr;
hr = HRESULT_FROM_WIN32( Status );
_pManApp->GetRsopContext()->DisableRsop( hr ); } }
return Status; }
void CAppInfo::LogUpgrades( CPolicyRecord* pRecord ) { if (!_Upgrades) { return; }
WCHAR** rgwszUpgradeable; WCHAR** rgwszReplaceable;
rgwszUpgradeable = NULL; rgwszReplaceable = NULL;
rgwszUpgradeable = new WCHAR*[_Upgrades];
if ( ! rgwszUpgradeable ) { goto ExitAndCleanup_LogUpgrades; }
rgwszReplaceable = new WCHAR*[_Upgrades];
if ( ! rgwszUpgradeable ) { goto ExitAndCleanup_LogUpgrades; }
RtlZeroMemory(rgwszUpgradeable, _Upgrades * sizeof(*rgwszUpgradeable)); RtlZeroMemory(rgwszReplaceable, _Upgrades * sizeof(*rgwszReplaceable));
DWORD cUpgradeable; DWORD cReplaceable;
cUpgradeable = 0; cReplaceable = 0;
//
// We will iterate through each upgrade so that we can log it
//
DWORD iUpgrade;
for (iUpgrade = 0; iUpgrade < _Upgrades; iUpgrade++) { WCHAR** ppwszUpgradeId;
//
// We only track the applications that we upgrade, not those
// that we are upgraded by. Note that we do not check for the
// converse flag, UPGRADE_OVER, since it can get cleared when
// an upgrade relationship is reversed -- we still need to
// log that as an upgrade for this app
//
if ( _pUpgrades[iUpgrade].Flags & UPGRADE_BY ) { continue; }
//
// If this upgrade is the result of a reversed upgrade due to
// a policy precedence violation, we should not log it as part
// of this application's upgrades
//
if ( _pUpgrades[iUpgrade].Flags & UPGRADE_REVERSED ) { continue; }
if ( _pUpgrades[iUpgrade].Flags & UPGRADE_UNINSTALL ) { ppwszUpgradeId = &(rgwszReplaceable[cReplaceable]); cReplaceable++; } else { ppwszUpgradeId = &(rgwszUpgradeable[cUpgradeable]); cUpgradeable++; }
//
// The RSoP schema requires the guids be in string form,
// so we need to convert this guid to a string -- note that
// this call allocates memory which must be freed later
//
GuidToString( _pUpgrades[iUpgrade].DeploymentId, ppwszUpgradeId);
if ( ! *ppwszUpgradeId ) { break; } }
if (iUpgrade == _Upgrades) { HRESULT hr;
hr = pRecord->SetValue( APP_ATTRIBUTE_UPGRADEABLE_APPLICATIONS, rgwszUpgradeable, cUpgradeable);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_UPGRADEABLE_APPLICATIONS, hr )
hr = pRecord->SetValue( APP_ATTRIBUTE_REPLACEABLE_APPLICATIONS, rgwszReplaceable, cReplaceable);
REPORT_ATTRIBUTE_SET_STATUS( APP_ATTRIBUTE_REPLACEABLE_APPLICATIONS, hr ) }
//
// Free the memory allocated in the GuidToString call for each upgrade guid
//
for (iUpgrade = 0; iUpgrade < cUpgradeable; iUpgrade++) { delete [] rgwszUpgradeable[iUpgrade]; }
for (iUpgrade = 0; iUpgrade < cReplaceable; iUpgrade++) { delete [] rgwszReplaceable[iUpgrade]; }
ExitAndCleanup_LogUpgrades:
delete [] rgwszUpgradeable; delete [] rgwszReplaceable; }
BOOL CAppInfo::IsLocal() { return NULL == _pwszGPODSPath; }
BOOL CAppInfo::IsGpoInScope() { BOOL bGpoInScope;
bGpoInScope = FALSE;
if ( _pwszGPOId ) { CGPOInfoList& GpoInfoList = _pManApp->GPOList();
bGpoInScope = ( NULL != GpoInfoList.Find( _pwszGPOId ) ); }
return bGpoInScope; }
LONG CAppInfo::InitializeRSOPTransformsList( PACKAGEDISPINFO* pPackageInfo ) { if ( ! pPackageInfo->cTransforms ) { return ERROR_SUCCESS; }
//
// The first element of the transforms list is
// actually the original package itself, so we don't count it
//
_cTransforms = pPackageInfo->cTransforms - 1;
//
// If we have transforms, get space for them
//
if ( 0 != _cTransforms ) { //
// The elements in the transform list are
// the actual transforms, so we'll copy them to our own list -- first
// allocate enough space for pointers to each path
//
_rgwszTransforms = new WCHAR* [ _cTransforms ]; if ( ! _rgwszTransforms ) { return ERROR_NOT_ENOUGH_MEMORY; }
RtlZeroMemory( _rgwszTransforms, sizeof(*_rgwszTransforms) * _cTransforms ); }
//
// Now place the transforms into the array
//
//
// Each transform is of the form <index>:<path>. The <index> is
// an integer that is unique for each transform in this package and
// less than or equal to the number of transforms. The 0th transform
// is actually not a transform, but the source package itself, and this
// is special cased
//
//
// Copy each transform path to an element in the array of strings. We
// decide which element in the array based on the <index> indicated
// in the transform string, shifted down 1 to skip the 0th (the source package).
// For example, the transform with <index> 1 goes to array element 0,
// the transform with <index> 4 goes to 3, etc. That way, no matter what
// order the transforms are in (e.g. 3,2,0,4,1), because we are mapping
// based on index, we end up with an ordered array (e.g. 1,2,3,4).
//
DWORD iTransform; BOOL bFoundSource;
bFoundSource = FALSE;
for ( iTransform = 0; iTransform < pPackageInfo->cTransforms; iTransform++ ) { WCHAR* wszTransform; DWORD dwTransformIndex;
//
// First, we need the transform index -- look for the separator
// so we can find it
//
wszTransform = wcschr( pPackageInfo->prgTransforms[ iTransform ], L':' );
//
// Check for bogus data
//
if ( ! wszTransform ) { return ERROR_INVALID_PARAMETER; }
//
// Truncate the string right after the index
//
*wszTransform = L'\0';
//
// The actual transform path starts one past the separator
//
wszTransform++;
//
// Now convert the index to integer
//
UNICODE_STRING TransformIndex; NTSTATUS NtStatus;
RtlInitUnicodeString( &TransformIndex, pPackageInfo->prgTransforms[ iTransform ] );
NtStatus = RtlUnicodeStringToInteger( &TransformIndex, 10, &dwTransformIndex );
//
// This should only fail if the index string is corrupt ( i.e. the number is not in base 10 )
//
if ( ! NT_SUCCESS( NtStatus ) ) { return RtlNtStatusToDosError( NtStatus ); }
//
// The number is correct syntactically, now ensure that semantically it is correct --
// the index cannot exceed the number of transforms.
//
if ( dwTransformIndex > _cTransforms ) { return ERROR_INVALID_PARAMETER; } //
// Check for the source package -- it is transform index 0
//
if ( 0 == dwTransformIndex ) { //
// Make sure the source is not listed twice
//
if ( bFoundSource ) { return ERROR_INVALID_PARAMETER; }
bFoundSource = TRUE;
//
// Copy the source package path, minus the prefix,
// to the member reserved for this purpose -- it currently
// points to the separator, so we need to go 1 past it
//
_pwszPackageLocation = StringDuplicate( wszTransform ); if ( ! _pwszPackageLocation ) { return ERROR_NOT_ENOUGH_MEMORY; }
continue; }
//
// Shift it down since this index's value includes the source package's
// occupation of zero, and we want to exclude it
//
dwTransformIndex--;
//
// Make sure we don't already have a transform at this index -- if we do, this
// is an ill-formed transform list
//
if ( _rgwszTransforms [ dwTransformIndex ] ) { return ERROR_INVALID_PARAMETER; }
_rgwszTransforms[ dwTransformIndex ] = StringDuplicate( wszTransform );
if ( ! _rgwszTransforms[ dwTransformIndex ] ) { return ERROR_NOT_ENOUGH_MEMORY; } }
//
// If we did not find a source, this is not a valid transform list
//
if ( ! bFoundSource ) { return ERROR_INVALID_PARAMETER; }
return S_OK; }
LONG CAppInfo::InitializeRSOPArchitectureInfo( PACKAGEDISPINFO* pPackageInfo ) { DWORD iArchitecture;
_rgArchitectures = new LONG [ pPackageInfo->cArchitectures ];
if ( ! _rgArchitectures ) { return ERROR_OUTOFMEMORY; }
_cArchitectures = pPackageInfo->cArchitectures; for ( iArchitecture = 0; iArchitecture < _cArchitectures; iArchitecture ++ ) { //
// We need to extract the Win32 processor architecture --
// the element in the PACKAGEDISPINFO is actually a combination
// of several other attributes -- the processor architecture is
// in highest 8 bits, so we'll shift everything right to get it.
//
_rgArchitectures[ iArchitecture ] = pPackageInfo->prgArchitectures[ iArchitecture ] >> 24;
//
// In planning mode, we determine architecture by seeing if it lists 64-bit --
// if so, we mark it as 64 bit. If it doesn't list 64-bit, it will be marked 32-bit
// if it lists 32-bit. If it doesn't list either of those, it will be
// marked as unknown.
//
if ((PROCESSOR_ARCHITECTURE_IA64 != _PrimaryArchitecture) && (PROCESSOR_ARCHITECTURE_AMD64 != _PrimaryArchitecture)) { if ((PROCESSOR_ARCHITECTURE_INTEL == _rgArchitectures[ iArchitecture ]) || (PROCESSOR_ARCHITECTURE_AMD64 == _rgArchitectures[ iArchitecture ]) || (PROCESSOR_ARCHITECTURE_IA64 == _rgArchitectures[ iArchitecture ])) { _PrimaryArchitecture = _rgArchitectures[ iArchitecture ]; } } }
if ( _pManApp->GetRsopContext()->IsDiagnosticModeEnabled() ) { _PrimaryArchitecture = pPackageInfo->MatchedArchitecture >> 24; }
return ERROR_SUCCESS; }
LONG CAppInfo::InitializeCategoriesList( PACKAGEDISPINFO* pPackageInfo ) { if ( ! pPackageInfo->cCategories ) { return ERROR_SUCCESS; }
//
// Reserve enough space so that we can have a pointer
// to each category guid string
//
_cCategories = pPackageInfo->cCategories;
_rgwszCategories = new WCHAR* [ pPackageInfo->cCategories ];
if ( ! _rgwszCategories ) { return ERROR_NOT_ENOUGH_MEMORY; }
RtlZeroMemory( _rgwszCategories, sizeof( *_rgwszCategories ) * _cCategories );
//
// Now reserve enough space for each copy of the category
// guid strings
//
DWORD iCategory;
for ( iCategory = 0; iCategory < _cCategories; iCategory++ ) { _rgwszCategories[ iCategory ] = StringDuplicate( pPackageInfo->prgCategories[ iCategory ] );
if ( ! _rgwszCategories[ iCategory ] ) { return ERROR_NOT_ENOUGH_MEMORY; } }
return S_OK; }
WCHAR* CAppInfo::GetRsopAppCriteria() { //
// We will want to specify an instance that has the conflict id unique
// to this package and its conflicts, and has an "installed" entry type.
//
//
// First, we calculate the length -- this is actually a constant based
// on the maximum sizes of the 2 criteria mentioned above: the conflict id
// is a guid string, and thus has a fixed maximum. The entry type is a
// 32 bit quantities, which of course has a maximum string length
// in decimal notation
//
DWORD cCriteriaLen;
cCriteriaLen = MAXLEN_RSOPREMOVAL_QUERY_CRITERIA + MAXLEN_RSOPENTRYTYPE_DECIMAL_REPRESENTATION + MAXLEN_RSOPPACKAGEID_GUID_REPRESENTATION;
WCHAR* wszCriteria = new WCHAR [ cCriteriaLen ];
if ( wszCriteria ) { WCHAR wszDeploymentId [ MAX_SZGUID_LEN ]; HRESULT hr;
GuidToString( _DeploymentId, wszDeploymentId);
hr = StringCchPrintf( wszCriteria, cCriteriaLen, RSOP_REMOVAL_QUERY_CRITERIA, APP_ATTRIBUTE_ENTRYTYPE_VALUE_INSTALLED_PACKAGE, wszDeploymentId); if (FAILED(hr)) { delete [] wszCriteria; return NULL; } }
return wszCriteria; }
DWORD CAppInfo::CopyScriptIfNeeded() { WCHAR * pwszTempScript; DWORD Length; DWORD Status;
if ( _State & APPSTATE_SCRIPT_PRESENT ) return ERROR_SUCCESS;
Status = ERROR_SUCCESS;
//
// It is remotely possible that we could hit this. For instance, during
// an ARP readvertise of an uninstalled assigned app. When using roaming
// profiles & with policy failing to run at the last logon, we might not
// have the local script, so we get this far, but this member will not be
// set when run in the service.
//
if ( ! _pwszGPTScriptPath ) return ERROR_BAD_PATHNAME;
//
// When doing user policy we have to do two copies to get the script in the
// right place. First we copy to a temp file while impersonating. This is
// so our sysvol access is done as the user. Then we copy from the temp file
// to the ACLed script dir under %systemroot% while reverted as LocalSystem.
//
if ( _pManApp->IsUserPolicy() ) { pwszTempScript = 0;
Length = GetTempPath( 0, NULL );
if ( Length > 0 ) { pwszTempScript = new WCHAR[Length + 1 + GUIDSTRLEN + 1];
if ( pwszTempScript ) { if ( 0 == GetTempPath( Length, pwszTempScript ) ) Status = GetLastError(); } else { Status = ERROR_OUTOFMEMORY; } } else Status = GetLastError();
if ( ERROR_SUCCESS == Status ) { if ( pwszTempScript[lstrlen(pwszTempScript)-1] != L'\\' ) { HRESULT hr; hr = StringCchCat( pwszTempScript, Length + 1 + GUIDSTRLEN + 1, L"\\" ); if (FAILED(hr)) { Status = HRESULT_CODE(hr); } } if (ERROR_SUCCESS == Status) { GuidToString( _DeploymentId, &pwszTempScript[lstrlen(pwszTempScript)]);
//
// CopyFile does not use the thread impersonation token for access checks,
// so we are ok copying into the system temp dir.
//
if ( ! CopyFile(_pwszGPTScriptPath, pwszTempScript, FALSE) ) { Status = GetLastError(); DebugMsg((DM_WARNING, IDS_SCRIPT_COPY_FAIL, _pwszDeploymentName, _pwszGPOName, _pwszGPTScriptPath, pwszTempScript, Status)); } } }
if ( ERROR_SUCCESS == Status ) { _pManApp->Revert();
if ( ! CopyFile(pwszTempScript, _pwszLocalScriptPath, FALSE) ) { Status = GetLastError(); DebugMsg((DM_WARNING, IDS_SCRIPT_COPY_FAIL, _pwszDeploymentName, _pwszGPOName, pwszTempScript, _pwszLocalScriptPath, Status)); }
DeleteFile( pwszTempScript );
DWORD impStatus = _pManApp->Impersonate(); if ( (impStatus != ERROR_SUCCESS) && (Status == ERROR_SUCCESS) ) { Status = impStatus; } }
delete [] pwszTempScript; } else { if ( ! CopyFile(_pwszGPTScriptPath, _pwszLocalScriptPath, FALSE) ) { Status = GetLastError(); DebugMsg((DM_WARNING, IDS_SCRIPT_COPY_FAIL, _pwszDeploymentName, _pwszGPOName, _pwszGPTScriptPath, _pwszLocalScriptPath, Status)); } }
if ( ERROR_SUCCESS == Status ) _State |= APPSTATE_SCRIPT_PRESENT;
return Status; }
void CAppInfo::CheckScriptExistence() { if ( _State & (APPSTATE_SCRIPT_EXISTED | APPSTATE_SCRIPT_NOT_EXISTED) ) return;
if ( _pManApp->ScriptList().Find( _DeploymentId ) != NULL ) _State |= APPSTATE_SCRIPT_EXISTED | APPSTATE_SCRIPT_PRESENT; else _State |= APPSTATE_SCRIPT_NOT_EXISTED; }
DWORD CAppInfo::EnforceAssignmentSecurity( BOOL * pbDidUninstall ) { INSTALLSTATE InstallState; WCHAR wszBuffer[8]; DWORD Size; DWORD Status; BOOL bPerMachine; BOOL bUninstall;
*pbDidUninstall = FALSE;
if ( ! _bNeedsUnmanagedRemove ) return ERROR_SUCCESS;
DebugMsg((DM_VERBOSE, IDS_ENFORCE_SECURE_ON, _pwszDeploymentName, _pwszGPOName));
InstallState = (*gpfnMsiQueryProductState)( _pwszProductId );
//
// If the app is not installed for the user/machine then we are done.
// Note that if only advertised, our subsequent advertise will update
// the source path, so we return in that case.
//
if ( ! AppPresent( InstallState) || (INSTALLSTATE_ADVERTISED == InstallState) ) return ERROR_SUCCESS;
Size = sizeof(wszBuffer) / sizeof(WCHAR);
Status = (*gpfnMsiGetProductInfo)( _pwszProductId, INSTALLPROPERTY_ASSIGNMENTTYPE, wszBuffer, &Size );
if ( Status != ERROR_SUCCESS ) { DebugMsg((DM_WARNING, IDS_ENFORCE_SECURE_FAIL, _pwszDeploymentName, Status)); return Status; }
bPerMachine = (L'1' == wszBuffer[0]);
//
// For user policy we only care about user installed apps and for machine
// policy we only care about machine installed apps.
//
if ( (_pManApp->IsUserPolicy() && bPerMachine) || (! _pManApp->IsUserPolicy() && ! bPerMachine) ) return ERROR_SUCCESS;
//
// If the app is present for the user and we've previously
// assigned it, we are safe if the product is marked for elevated
// install.
//
// If we've previously installed a machine assigned app then it's ok,
// because by definition all machine installed apps are elevated
// (because they require admin priviledge to install), so we
// don't need this extra check for them.
//
if ( _State & APPSTATE_SCRIPT_EXISTED ) { BOOL bElevated;
if ( _pManApp->IsUserPolicy() ) { Status = (*gpfnMsiIsProductElevated)( _pwszProductId, &bElevated ); if ( Status != ERROR_SUCCESS ) { DebugMsg((DM_WARNING, IDS_ENFORCE_SECURE_FAIL, _pwszDeploymentName, Status)); return Status; } } else { bElevated = TRUE; }
if ( bElevated ) return ERROR_SUCCESS; }
gpEvents->RemoveUnmanaged( this ); _pManApp->LogonMsgInstall( _pwszDeploymentName ); Status = Uninstall(); _pManApp->LogonMsgApplying();
if ( ERROR_SUCCESS == Status ) { _InstallState = INSTALLSTATE_ABSENT; *pbDidUninstall = TRUE; } else { DebugMsg((DM_WARNING, IDS_ENFORCE_SECURE_FAIL, _pwszDeploymentName, Status)); }
return Status; }
BOOL CAppInfo::RequiresUnmanagedRemoval() { BOOL bRequiresUnmanagedRemoval = FALSE;
if ( _pManApp->ARPList() ) { return FALSE; }
//
// We remove unmanaged installs as a security precaution to
// prevent elevation of privileges --
// this means that we do not need to do it for machine assigned apps
// or per-user apps for admins, since since system and admins
// already have the highest privileges
//
// Note that we could do this for all apps (whether or not they
// are machine assigned or user assigned / published to an admin),
// but the unmanaged removal is a less enjoyable user experience,
// so we only want to do this in the case where it is required --
// the per-user, non-admin case where the unmanaged application
// is not already elevated
//
//
// Verify that this is a per-user app
//
if ( _pManApp->IsUserPolicy() ) { //
// For planning mode, we'll need to choose a "default" behavior
// so that we can simulate what might happen if this application
// were to be applied to a machine
//
if ( _pManApp->GetRsopContext()->IsPlanningModeEnabled() ) { //
// We will err on the side of safety and say that this
// simulated application may require removal of unmanaged
// installs
//
bRequiresUnmanagedRemoval = TRUE; } else { //
// If this user is not an admin, we may require
// the removal of an unmanaged install of this app
// if one exists
//
if ( ! IsMemberOfAdminGroup( _pManApp->UserToken() ) ) { BOOL bIsProductElevated = FALSE; DWORD StatusElevated;
//
// The last check -- is this app present as an elevated install? If not,
// we should require its uninstall. Elevated apps were placed here by some
// admin user, so we do not consider these a threat to the system.
//
// Note that this call requires that the caller is impersonating
//
StatusElevated = gpfnMsiIsProductElevated( _pwszProductId, &bIsProductElevated );
//
// If this unmanaged install is not elevated, or if we were unable to determine whether
// or not it was elevated, we will require that the application is removed if it
// exists on the machine
//
if ( ( ERROR_SUCCESS != StatusElevated ) || ! bIsProductElevated ) { bRequiresUnmanagedRemoval = TRUE; } } } }
return bRequiresUnmanagedRemoval; }
DWORD CAppInfo::RollbackUpgrades() { DWORD Status;
for ( DWORD n = 0; n < _Upgrades; n++ ) { Status = ERROR_SUCCESS;
if ( ! _pUpgrades[n].pBaseApp || ! (_pUpgrades[n].Flags & UPGRADE_OVER) ) continue;
// Skip it if the app didn't exist here to begin with.
if ( ! (_pUpgrades[n].pBaseApp->_State & (APPSTATE_PUBLISHED | APPSTATE_ASSIGNED)) ) continue;
if ( _Status != ERROR_SUCCESS ) Status = _Status; else Status = _pUpgrades[n].pBaseApp->_Status;
gpEvents->UpgradeAbort( Status, this, _pUpgrades[n].pBaseApp, ERROR_SUCCESS == _Status );
// Re-apply any app which was successfully removed.
if ( ERROR_SUCCESS == _pUpgrades[n].pBaseApp->_Status ) { DWORD ScriptFlags = SCRIPTFLAGS_REGDATA_CNFGINFO | SCRIPTFLAGS_CACHEINFO | SCRIPTFLAGS_SHORTCUTS; DWORD AssignStatus;
if ( _pUpgrades[n].pBaseApp->_State & APPSTATE_ASSIGNED ) ScriptFlags |= SCRIPTFLAGS_REGDATA_EXTENSIONINFO;
AssignStatus = _pUpgrades[n].pBaseApp->Assign( ScriptFlags, TRUE, TRUE );
//
// Here we are checking for any assigned apps which are configured for
// default install. If such an app was previously in an install state
// before the upgrade attempt, then we put it back into this state by
// doing a default install again.
//
if ( (ERROR_SUCCESS == AssignStatus) && (! _pManApp->IsUserPolicy() || (_pUpgrades[n].pBaseApp->_State & APPSTATE_INSTALL)) && (AppPresent(_pUpgrades[n].pBaseApp->_InstallState) && (_pUpgrades[n].pBaseApp->_InstallState != INSTALLSTATE_ADVERTISED)) ) { (void) _pUpgrades[n].pBaseApp->Install(); }
if ( ERROR_SUCCESS == AssignStatus ) { _pUpgrades[n].pBaseApp->_bRollback = TRUE; } } }
// Each upgrade rollback has its own status set ...
Status = ERROR_SUCCESS;
if ( (_State & APPSTATE_SCRIPT_NOT_EXISTED) && (_State & APPSTATE_SCRIPT_PRESENT) && _pwszLocalScriptPath ) { //
// Remove the local script for the upgrade app since this app is not currently applied --
// we need to revert since the user does not have rights in this directory.
//
_pManApp->Revert();
DeleteFile( _pwszLocalScriptPath );
Status = _pManApp->Impersonate(); }
return Status; }
BOOL CAppInfo::CopyToApplicationInfo( APPLICATION_INFO * pApplicationInfo ) { GuidToString( _DeploymentId, &pApplicationInfo->pwszDeploymentId ); pApplicationInfo->pwszDeploymentName = StringDuplicate( _pwszDeploymentName ); pApplicationInfo->pwszGPOName = StringDuplicate( _pwszGPOName ); pApplicationInfo->pwszProductCode = StringDuplicate( _pwszProductId ); pApplicationInfo->pwszDescriptor = 0; pApplicationInfo->pwszSetupCommand = 0; pApplicationInfo->Flags = 0;
if ( INSTALLUILEVEL_FULL == _InstallUILevel ) pApplicationInfo->Flags = APPINFOFLAG_FULLUI; else pApplicationInfo->Flags = APPINFOFLAG_BASICUI;
if ( _State & (APPSTATE_ASSIGNED | APPSTATE_PUBLISHED) ) pApplicationInfo->Flags |= APPINFOFLAG_ALREADYMANAGED;
if ( ACTION_UNINSTALL == _Action ) pApplicationInfo->Flags |= APPINFOFLAG_UNINSTALL; else if ( ACTION_ORPHAN == _Action ) pApplicationInfo->Flags |= APPINFOFLAG_ORPHAN;
if ( ! pApplicationInfo->pwszDeploymentId || ! pApplicationInfo->pwszDeploymentName || ! pApplicationInfo->pwszGPOName || ! pApplicationInfo->pwszProductCode ) return FALSE;
return TRUE; }
void CAppInfo::AddToOverrideList( GUID * pDeploymentId ) { GUID * pOldList;
if ( _pManApp->ARPList() ) return;
pOldList = _pOverrides;
_pOverrides = new GUID[_Overrides+1]; if ( ! _pOverrides ) { _pOverrides = pOldList; return; }
if ( _Overrides > 0 ) { memcpy( _pOverrides, pOldList, _Overrides * sizeof(GUID) ); delete pOldList; }
memcpy( &_pOverrides[_Overrides++], pDeploymentId, sizeof(GUID) ); }
DWORD CallMsiConfigureProduct( WCHAR * pwszProduct, int InstallLevel, INSTALLSTATE InstallState, WCHAR * pwszCommandLine ) { DWORD Status;
Status = (*gpfnMsiConfigureProductEx)( pwszProduct, InstallLevel, InstallState, pwszCommandLine );
REMAP_DARWIN_STATUS( Status );
return Status; }
DWORD CallMsiReinstallProduct( WCHAR * pwszProduct ) { DWORD Status;
Status = (*gpfnMsiReinstallProduct)( pwszProduct, REINSTALLMODE_FILEOLDERVERSION | REINSTALLMODE_PACKAGE | REINSTALLMODE_MACHINEDATA | REINSTALLMODE_USERDATA | REINSTALLMODE_SHORTCUT );
REMAP_DARWIN_STATUS( Status );
return Status; }
DWORD CallMsiAdvertiseScript( WCHAR * pwszScriptFile, DWORD Flags, PHKEY phkClasses, BOOL bRemoveItems ) { return (*gpfnMsiAdvertiseScript)( pwszScriptFile, Flags, phkClasses, bRemoveItems ); }
|