Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

4872 lines
153 KiB

//*************************************************************
//
// 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 );
}