windows-nt/Source/XPSP1/NT/admin/snapin/schmmgmt/attrgen.cpp

1266 lines
34 KiB
C++
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
#include "stdafx.h"
#include "compdata.h"
#include "cmponent.h"
#include "attrgen.hpp"
#include "resource.h"
#include "dataobj.h"
const TCHAR szUserClass[] = USER_CLASS_NAME ;
// disable User Copy for the following list of attributes
const TCHAR * rgszExclClass[] = { _T("cn"), _T("displayName"),
_T("sAMAccountName"), _T("userAccountControl"),
_T("userParameters"), _T("userPrincipalName"),
NULL };
//
// The following table is copied from dirsync\ldifds\ldifldap\samrestrict.h
//
// CLASS_USER, SampUserObjectType (ldapdisplayname: user)
//
PCWSTR rgszExclClass2[] = { // user[] = {
L"memberOf", // SAMP_USER_GROUPS, ATT_MEMBER
L"dBCSPwd", // SAMP_USER_DBCS_PWD, ATT_DBCS_PWD
L"ntPwdHistory", // SAMP_USER_NT_PWD_HISTORY, ATT_NT_PWD_HISTORY
L"lmPwdHistory", // SAMP_USER_LM_PWD_HISTORY, ATT_LM_PWD_HISTORY
L"lastLogon", // SAMP_FIXED_USER_LAST_LOGON, ATT_LAST_LOGON
L"lastLogoff", // SAMP_FIXED_USER_LAST_LOGOFF, ATT_LAST_LOGOFF
L"badPasswordTime", // SAMP_FIXED_USER_LAST_BAD_PASSWORD_TIME,
// ATT_BAD_PASSWORD_TIME
L"rid", // SAMP_FIXED_USER_USERID, ATT_RID
L"badPwdCount", // SAMP_FIXED_USER_BAD_PWD_COUNT,
// ATT_BAD_PWD_COUNT
L"logonCount", // SAMP_FIXED_USER_LOGON_COUNT, ATT_LOGON_COUNT
L"sAMAccountType", // SAMP_USER_ACCOUNT_TYPE, ATT_SAM_ACCOUNT_TYPE
L"supplementalCredentials", // SAMP_FIXED_USER_SUPPLEMENTAL_CREDENTIALS,
// ATT_SUPPLEMENTAL_CREDENTIALS
L"objectSid", // not in mappings.c, but still required!,
// ATT_OBJECT_SID
L"pwdLastSet",
NULL
};
const TCHAR szTopClass[] = _T("Top");
const CDialogControlsInfo ctrls[] =
{
{ IDC_ATTRIB_GENERAL_DESCRIPTION_EDIT, g_Description, TRUE },
{ IDC_ATTRIB_GENERAL_MIN_EDIT, g_RangeLower, TRUE },
{ IDC_ATTRIB_GENERAL_MAX_EDIT, g_RangeUpper, TRUE },
{ IDC_ATTRIB_GENERAL_DISPLAYABLE_CHECK, g_ShowInAdvViewOnly, FALSE },
{ IDC_ATTRIB_GENERAL_DEACTIVATE, g_isDefunct, FALSE },
{ IDC_ATTRIB_GENERAL_INDEX_CHECK, g_IndexFlag, FALSE },
{ IDC_ATTRIB_GENERAL_REPLICATED, g_GCReplicated, FALSE },
{ IDC_ATTRIB_GENERAL_CPYATTR_CHECK, g_IndexFlag, FALSE },
{ IDC_ATTRIB_GENERAL_ANR_CHECK, g_IndexFlag, FALSE },
{ IDC_ATTRIB_GENERAL_CONTAINERIZED_INDEX_CHECK, g_IndexFlag, FALSE},
} ;
const DWORD AttributeGeneralPage::help_map[] =
{
IDI_ATTRIBUTE, NO_HELP,
IDC_ATTRIB_GENERAL_NAME_STATIC, NO_HELP,
IDC_ATTRIB_GENERAL_DESCRIPTION_EDIT, IDH_ATTRIB_GENERAL_DESCRIPTION_EDIT,
IDC_ATTRIB_GENERAL_LDN, IDH_ATTRIB_GENERAL_LDN,
IDC_ATTRIB_GENERAL_OID_EDIT, IDH_ATTRIB_GENERAL_OID_EDIT,
IDC_ATTRIB_GENERAL_VALUE_STATIC, NO_HELP,
IDC_ATTRIB_GENERAL_SYNTAX_EDIT, IDH_ATTRIB_GENERAL_SYNTAX_EDIT,
IDC_ATTRIB_GENERAL_MIN_EDIT, IDH_ATTRIB_GENERAL_MIN_EDIT,
IDC_ATTRIB_GENERAL_MAX_EDIT, IDH_ATTRIB_GENERAL_MAX_EDIT,
IDC_ATTRIB_GENERAL_DISPLAYABLE_CHECK, IDH_ATTRIB_GENERAL_DISPLAYABLE_CHECK,
IDC_ATTRIB_GENERAL_DEACTIVATE, IDH_ATTRIB_DEACTIVATE,
IDC_ATTRIB_GENERAL_INDEX_CHECK, IDH_ATTRIB_GENERAL_INDEX_CHECK,
IDC_ATTRIB_GENERAL_CONTAINERIZED_INDEX_CHECK, IDH_ATTRIB_GENERAL_CONTAINERIZED_INDEX_CHECK,
IDC_ATTRIB_GENERAL_ANR_CHECK, IDH_ATTRIB_GENERAL_ANR_CHECK,
IDC_ATTRIB_GENERAL_REPLICATED, IDH_REPLICATED,
IDC_ATTRIB_GENERAL_CPYATTR_CHECK, IDH_ATTRIB_GENERAL_CPYATTR_CHECK,
IDC_ATTRIB_GENERAL_SYSCLASS_STATIC, NO_HELP,
0, 0
};
// returns state of bit n
inline
bool
getbit(const DWORD& bits, int n)
{
return (bits & (1 << n)) ? true : false;
}
// sets bit n to 1
inline
void
setbit(DWORD& bits, int n)
{
bits |= (1 << n);
}
// sets bit n to 0
inline
void
clearbit(DWORD& bits, int n)
{
bits &= ~(1 << n);
}
//
// Attribute property sheet routines.
//
BEGIN_MESSAGE_MAP( AttributeGeneralPage, CDialog )
ON_MESSAGE(WM_HELP, OnHelp)
ON_MESSAGE(WM_CONTEXTMENU, OnContextHelp)
ON_BN_CLICKED(IDC_ATTRIB_GENERAL_INDEX_CHECK, OnIndexClick)
ON_BN_CLICKED(IDC_ATTRIB_GENERAL_DEACTIVATE, OnDeactivateClick)
END_MESSAGE_MAP()
AttributeGeneralPage::AttributeGeneralPage(
Component* pResultControl,
LPDATAOBJECT lpDataObject)
:
CPropertyPage( IDD_ATTRIB_GENERAL ),
pCookie( NULL ),
pIADsObject( NULL ),
pObject( NULL),
lpResultDataObject( lpDataObject ),
pComponent( pResultControl ),
fDataLoaded( FALSE ),
Displayable( TRUE ),
DDXDisplayable( TRUE ),
search_flags(0),
DDXIndexed( FALSE ),
DDXANR( FALSE ),
DDXCopyOnDuplicate( FALSE ),
Defunct( FALSE ),
DDXDefunct( FALSE ),
ReplicatedToGC( FALSE ),
DDXReplicatedToGC( FALSE ),
DDXContainerIndexed( FALSE ),
m_editLowerRange( CParsedEdit::EDIT_TYPE_UINT32 ),
m_editUpperRange( CParsedEdit::EDIT_TYPE_UINT32 )
{
}
BOOL
AttributeGeneralPage::OnSetActive()
{
// If pIADsObject is NULL, close dialog box
if( CPropertyPage::OnSetActive() )
{
if ( !pIADsObject )
{
return FALSE;
}
else
{
// always enable the Apply button
SetModified(TRUE);
return TRUE;
}
}
else
return FALSE;
}
AttributeGeneralPage::~AttributeGeneralPage(
)
{
ComponentData& Scope = pComponent->QueryComponentDataRef();
//
// Always make sure we free the IADs object.
//
if ( pIADsObject ) {
pIADsObject->Release();
pIADsObject = NULL;
}
//
// And release the cache.
//
if ( pObject ) {
Scope.g_SchemaCache.ReleaseRef( pObject );
}
}
BOOL
AttributeGeneralPage::OnInitDialog()
{
CPropertyPage::OnInitDialog();
ASSERT( GetDlgItem(IDC_ATTRIB_GENERAL_DESCRIPTION_EDIT) );
( static_cast<CEdit *>( GetDlgItem(IDC_ATTRIB_GENERAL_DESCRIPTION_EDIT) ) )
-> LimitText( 1024 ) ;
m_editLowerRange.SubclassEdit(IDC_ATTRIB_GENERAL_MIN_EDIT, this, cchMinMaxRange);
m_editUpperRange.SubclassEdit(IDC_ATTRIB_GENERAL_MAX_EDIT, this, cchMinMaxRange);
return TRUE;
}
void
AttributeGeneralPage::Load(
Cookie& CookieRef
) {
//
// Store the cookie object pointer. Everything
// else gets loaded when the page is displayed.
//
pCookie = &CookieRef;
return;
}
BOOL
AttributeGeneralPage::OnApply(
VOID
) {
HRESULT hr;
VARIANT AdsValue;
BOOL fChangesMade = FALSE;
BOOL fRangeChange = FALSE;
BOOL fApplyAbort = FALSE; // stop later saves
BOOL fApplyFailed = FALSE; // should not close the box
DWORD dwRange;
// Enable hourglass
CWaitCursor wait;
//
// We have to flush the IADS property cache if we
// have a failure so later operations won't fail because
// of a bad cached attribute.
//
IADsPropertyList *pPropertyList;
hr = pIADsObject->QueryInterface( IID_IADsPropertyList,
reinterpret_cast<void**>(&pPropertyList) );
if ( FAILED( hr ) ) {
pPropertyList = NULL;
fApplyAbort = TRUE;
}
//
// Check to see if something we cared about changed.
// We care about Description, Min, Max, Indexed,
// Defunct, ReplicatedToGC, and Displayable.
//
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
VariantInit( &AdsValue );
//
// Defunct -- in case it was deactivated, activate the object first
//
if( !fApplyAbort && !DDXDefunct && DDXDefunct != Defunct )
{
hr = ChangeDefunctState( DDXDefunct, Defunct, pPropertyList, fApplyAbort, fApplyFailed );
}
//
// Description
//
if ( !fApplyAbort && DDXDescription != Description ) {
V_VT(&AdsValue) = VT_BSTR;
V_BSTR(&AdsValue) = const_cast<BSTR>((LPCTSTR)DDXDescription);
if ( DDXDescription.IsEmpty() ) {
hr = pIADsObject->PutEx( ADS_PROPERTY_CLEAR,
const_cast<BSTR>((LPCTSTR)g_Description),
AdsValue );
} else {
hr = pIADsObject->Put( const_cast<BSTR>((LPCTSTR)g_Description),
AdsValue );
}
ASSERT( SUCCEEDED( hr ) );
hr = pIADsObject->SetInfo();
if ( SUCCEEDED( hr ) ) {
pObject->description = DDXDescription;
fChangesMade = TRUE;
Description = DDXDescription;
} else {
pPropertyList->PurgePropertyList();
if( ERROR_DS_UNWILLING_TO_PERFORM == HRESULT_CODE(hr) )
{
fApplyFailed = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_EDIT_DESC );
}
else
{
fApplyAbort = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
}
}
VariantInit( &AdsValue );
}
//
// Displayable
//
if ( !fApplyAbort && DDXDisplayable != Displayable ) {
V_VT(&AdsValue) = VT_BOOL;
if ( DDXDisplayable ) {
V_BOOL(&AdsValue) = 0;
} else {
V_BOOL(&AdsValue) = -1;
}
hr = pIADsObject->Put(g_ShowInAdvViewOnly, AdsValue);
ASSERT( SUCCEEDED( hr ) );
hr = pIADsObject->SetInfo();
if ( FAILED( hr ) ) {
pPropertyList->PurgePropertyList();
if( ERROR_DS_UNWILLING_TO_PERFORM == HRESULT_CODE(hr) )
{
fApplyFailed = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_EDIT_DISPLAYABLE );
}
else
{
fApplyAbort = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
}
}
else
{
Displayable = DDXDisplayable;
}
VariantInit( &AdsValue );
}
//
// ReplicatedToGC
//
if ( !fApplyAbort && DDXReplicatedToGC != ReplicatedToGC ) {
V_VT(&AdsValue) = VT_BOOL;
if ( DDXReplicatedToGC ) {
V_BOOL(&AdsValue) = -1;
} else {
V_BOOL(&AdsValue) = 0;
}
hr = pIADsObject->Put( const_cast<BSTR>((LPCTSTR)g_GCReplicated),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
hr = pIADsObject->SetInfo();
if ( FAILED( hr ) ) {
pPropertyList->PurgePropertyList();
if( ERROR_DS_UNWILLING_TO_PERFORM == HRESULT_CODE(hr) )
{
fApplyFailed = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_EDIT_GC );
}
else
{
fApplyAbort = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
}
}
else
{
ReplicatedToGC = DDXReplicatedToGC;
}
VariantInit( &AdsValue );
}
//
// Indexed
//
// make sure ANR is not set when Indexed is unchecked
if( !DDXIndexed )
DDXANR = FALSE;
if( !fApplyAbort &&
(getbit(search_flags, INDEX_BIT_ATTINDEX) != (DDXIndexed ? 1 : 0)
|| getbit(search_flags, INDEX_BIT_ANR) != (DDXANR ? 1 : 0)
|| getbit(search_flags, INDEX_BIT_COPYONDUPLICATE) != (DDXCopyOnDuplicate ? 1 : 0)
|| getbit(search_flags, INDEX_BIT_PDNTATTINDEX) != (DDXContainerIndexed ? 1 : 0)) )
{
DWORD DDXsearch_flags = search_flags;
V_VT(&AdsValue) = VT_I4;
if (DDXIndexed)
setbit(DDXsearch_flags, INDEX_BIT_ATTINDEX);
else
clearbit(DDXsearch_flags, INDEX_BIT_ATTINDEX);
ASSERT( DDXIndexed || !DDXANR );
if (DDXANR)
setbit(DDXsearch_flags, INDEX_BIT_ANR);
else
clearbit(DDXsearch_flags, INDEX_BIT_ANR);
if (DDXCopyOnDuplicate)
setbit(DDXsearch_flags, INDEX_BIT_COPYONDUPLICATE);
else
clearbit(DDXsearch_flags, INDEX_BIT_COPYONDUPLICATE);
if (DDXContainerIndexed)
setbit(DDXsearch_flags, INDEX_BIT_PDNTATTINDEX);
else
clearbit(DDXsearch_flags, INDEX_BIT_PDNTATTINDEX);
V_I4(&AdsValue) = DDXsearch_flags;
hr = pIADsObject->Put( const_cast<BSTR>((LPCTSTR)g_IndexFlag),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
hr = pIADsObject->SetInfo();
if ( FAILED( hr ) ) {
pPropertyList->PurgePropertyList();
if( ERROR_DS_UNWILLING_TO_PERFORM == HRESULT_CODE(hr) )
{
fApplyFailed = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_EDIT_INDEXED );
}
else
{
fApplyAbort = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
}
}
else
{
search_flags = DDXsearch_flags;
}
VariantInit( &AdsValue );
}
//
// RangeUpper and RangeLower
// These have to be set together so the server
// range validation works correctly.
//
if ( !fApplyAbort && RangeUpper != DDXRangeUpper ) {
if ( DDXRangeUpper.IsEmpty() ) {
//
// Clear the value.
//
hr = pIADsObject->PutEx( ADS_PROPERTY_CLEAR,
const_cast<BSTR>((LPCTSTR)g_RangeUpper),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
} else {
//
// Store the new value.
//
ASSERT(pObject);
hr = GetSafeSignedDWORDFromString( this, dwRange, DDXRangeUpper,
g_Syntax[ pObject->SyntaxOrdinal ].m_fIsSigned );
ASSERT( S_OK == hr ); // validation should have taken care of min/max stuff
V_VT( &AdsValue ) = VT_I4;
V_I4( &AdsValue ) = dwRange;
hr = pIADsObject->Put( const_cast<BSTR>((LPCTSTR)g_RangeUpper),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
}
fRangeChange = TRUE;
VariantInit( &AdsValue );
}
if ( !fApplyAbort && RangeLower != DDXRangeLower ) {
if ( DDXRangeLower.IsEmpty() ) {
//
// Clear the value.
//
hr = pIADsObject->PutEx( ADS_PROPERTY_CLEAR,
const_cast<BSTR>((LPCTSTR)g_RangeLower),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
} else {
//
// Store the new value.
//
ASSERT(pObject);
hr = GetSafeSignedDWORDFromString( this, dwRange, DDXRangeLower,
g_Syntax[ pObject->SyntaxOrdinal ].m_fIsSigned );
ASSERT( S_OK == hr ); // validation should have taken care of min/max stuff
V_VT( &AdsValue ) = VT_I4;
V_I4( &AdsValue ) = dwRange;
hr = pIADsObject->Put( const_cast<BSTR>((LPCTSTR)g_RangeLower),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
}
fRangeChange = TRUE;
VariantInit( &AdsValue );
}
//
// Actually commit the changes.
//
if ( !fApplyAbort && fRangeChange ) {
hr = pIADsObject->SetInfo();
if ( FAILED( hr ) ) {
pPropertyList->PurgePropertyList();
if( ERROR_DS_UNWILLING_TO_PERFORM == HRESULT_CODE(hr) )
{
fApplyFailed = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_CREATE_MINMAX );
}
else
{
fApplyAbort = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
}
}
else
{
RangeLower = DDXRangeLower;
RangeUpper = DDXRangeUpper;
}
}
//
// Defunct -- in case it was active, deactivate the object after we are done update
//
if( !fApplyAbort && DDXDefunct && DDXDefunct != Defunct )
{
hr = ChangeDefunctState( DDXDefunct, Defunct, pPropertyList, fApplyAbort, fApplyFailed );
}
//
// If there are visible changes, update the views.
//
if ( ( fChangesMade ) &&
( pComponent ) &&
( lpResultDataObject ) ) {
CCookie* pBaseCookie;
Cookie* pCookie;
hr = ExtractData( lpResultDataObject,
CSchmMgmtDataObject::m_CFRawCookie,
OUT reinterpret_cast<PBYTE>(&pBaseCookie),
sizeof(pBaseCookie) );
ASSERT( SUCCEEDED(hr) );
pCookie = pComponent->ActiveCookie(pBaseCookie);
ASSERT( NULL != pCookie );
hr = pComponent->m_pResultData->UpdateItem( pCookie->hResultId );
ASSERT( SUCCEEDED(hr) );
}
if ( pPropertyList ) {
pPropertyList->Release();
}
return !fApplyAbort && !fApplyFailed ; // return TRUE if nothing happened
}
void
AttributeGeneralPage::DoDataExchange(
CDataExchange *pDX
) {
HRESULT hr;
CString szAdsPath;
VARIANT AdsResult;
UINT SyntaxOrdinal = SCHEMA_SYNTAX_UNKNOWN;
ComponentData& Scope = pComponent->QueryComponentDataRef();
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
// Enable hourglass
CWaitCursor wait;
CPropertyPage::DoDataExchange( pDX );
VariantInit( &AdsResult );
// We still want to do the DDX exchange at the bottom
// even if the data is already loaded so make it part
// of this if statement instead of short circuiting
// from within
if ( !pDX->m_bSaveAndValidate &&
!fDataLoaded) {
//
// Get the schema cache object and the actual ADS object.
// Keep the ADS object around while the page is loaded.
//
ASSERT( !pObject ); // Must be NULL initially
pObject = Scope.g_SchemaCache.LookupSchemaObjectByCN(
pCookie->strSchemaObject,
SCHMMGMT_ATTRIBUTE );
if ( pObject ) {
Scope.GetSchemaObjectPath( pObject->commonName, szAdsPath );
if ( !szAdsPath.IsEmpty() ) {
hr = ADsGetObject( (LPWSTR)(LPCWSTR)szAdsPath,
IID_IADs,
(void **)&pIADsObject );
if( SUCCEEDED(hr) )
{
BOOL fIsConstructed = FALSE;
// Igrnore error code
IsConstructedObject( pIADsObject, fIsConstructed );
// Enable check box if ths attribute is not in the excluded
// list and available for the User Class
GetDlgItem(IDC_ATTRIB_GENERAL_CPYATTR_CHECK)->EnableWindow(
!fIsConstructed &&
!IsInList( rgszExclClass, pObject->ldapDisplayName ) &&
!IsInList( rgszExclClass2, pObject->ldapDisplayName ) &&
IsAttributeInUserClass( pObject->ldapDisplayName ) );
}
}
}
//
// If we have no ADS object, we should error out!
//
if ( !pIADsObject )
{
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_NO_SCHEMA_OBJECT );
// Because there is no pIADsObject, OnSetActive() will close dialog box
return;
}
//
// ObjectName - Use the ldapDisplayName to be consistent
// with the other admin components.
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_DisplayName),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BSTR );
ObjectName = AdsResult.bstrVal;
VariantClear( &AdsResult );
}
//
// Description
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_Description),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BSTR );
Description = AdsResult.bstrVal;
DDXDescription = AdsResult.bstrVal;
VariantClear( &AdsResult );
}
//
// CommonName
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_CN),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BSTR );
DisplayName = AdsResult.bstrVal;
VariantClear( &AdsResult );
}
//
// SysClass
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_SystemOnly),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BOOL );
if ( AdsResult.boolVal ) {
SysClassString = g_SysAttrString;
} else {
SysClassString = L"";
}
VariantClear( &AdsResult );
} else {
SysClassString = L"";
}
//
// Syntax
//
// No need to reload from schema -- syntax never changes
//
ASSERT(pObject);
if( pObject )
SyntaxOrdinal = pObject->SyntaxOrdinal;
SyntaxString = g_Syntax[ SyntaxOrdinal ].m_strSyntaxName;
//
// Syntax min and max values.
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_RangeLower),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( V_VT( &AdsResult ) == VT_I4 );
RangeLower.Format( g_Syntax[ SyntaxOrdinal ].m_fIsSigned ?
g_INT32_FORMAT : g_UINT32_FORMAT,
V_I4( &AdsResult ) );
ASSERT( RangeLower.GetLength() <= cchMinMaxRange );
DDXRangeLower = RangeLower;
VariantClear( &AdsResult );
}
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_RangeUpper),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( V_VT( &AdsResult ) == VT_I4 );
RangeUpper.Format( g_Syntax[ SyntaxOrdinal ].m_fIsSigned ?
g_INT32_FORMAT : g_UINT32_FORMAT,
V_I4( &AdsResult ) );
ASSERT( RangeUpper.GetLength() <= cchMinMaxRange );
DDXRangeUpper = RangeUpper;
VariantClear( &AdsResult );
}
//
// Multi-Valued
//
MultiValued.LoadString( IDS_ATTRIBUTE_MULTI );
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_IsSingleValued),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BOOL );
if ( AdsResult.boolVal == -1 ) {
MultiValued.Empty();
MultiValued.LoadString( IDS_ATTRIBUTE_SINGLE );
}
VariantClear( &AdsResult );
}
//
// Displayable
//
hr = pIADsObject->Get(g_ShowInAdvViewOnly, &AdsResult);
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BOOL );
if ( AdsResult.boolVal == -1 ) {
Displayable = FALSE;
DDXDisplayable = FALSE;
}
VariantClear( &AdsResult );
}
//
// Defunct
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_isDefunct),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BOOL );
if ( AdsResult.boolVal == -1 ) {
Defunct = TRUE;
DDXDefunct = TRUE;
}
VariantClear( &AdsResult );
}
//
// ReplicatedToGC
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_GCReplicated),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BOOL );
if ( AdsResult.boolVal == -1 ) {
ReplicatedToGC = TRUE;
DDXReplicatedToGC = TRUE;
}
VariantClear( &AdsResult );
}
//
// OID
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_GlobalAttributeID),
&AdsResult );
if ( SUCCEEDED( hr ) ) {
ASSERT( AdsResult.vt == VT_BSTR );
OidString = AdsResult.bstrVal;
VariantClear( &AdsResult );
}
//
// Indexed, ANR, & Copy on duplicate
//
hr = pIADsObject->Get( const_cast<BSTR>((LPCTSTR)g_IndexFlag),
&AdsResult );
if (SUCCEEDED(hr))
{
ASSERT(AdsResult.vt == VT_I4);
search_flags = V_I4(&AdsResult);
// Index this attribute in the Active Directory
DDXIndexed = getbit( search_flags, INDEX_BIT_ATTINDEX );
// Ambiguous Name Resolution (ANR)
// checkbox must exist
ASSERT( GetDlgItem(IDC_ATTRIB_GENERAL_ANR_CHECK) );
// if not indexed, or not allowed, disable the checkbox
GetDlgItem(IDC_ATTRIB_GENERAL_ANR_CHECK)->EnableWindow(
g_Syntax[ SyntaxOrdinal ].m_fIsANRCapable ? DDXIndexed : FALSE );
if( DDXIndexed )
DDXANR = getbit( search_flags, INDEX_BIT_ANR );
else
{
DDXANR = FALSE;
// if not indexed, ANR in DS should not be set
ASSERT( !getbit( search_flags, INDEX_BIT_ANR ) );
}
// Attribute is copied when duplicating a user
DDXCopyOnDuplicate = getbit( search_flags, INDEX_BIT_COPYONDUPLICATE );
VariantClear( &AdsResult );
}
// Containerized index
DDXContainerIndexed = getbit( search_flags, INDEX_BIT_PDNTATTINDEX );
// Determine if this is a category 1 object & disable read-only fields
BOOL fIsSystemObject = FALSE;
hr = IsCategory1Object( pIADsObject, fIsSystemObject );
if( SUCCEEDED(hr) && fIsSystemObject )
{
ASSERT( GetDlgItem(IDC_ATTRIB_GENERAL_MIN_EDIT) );
ASSERT( GetDlgItem(IDC_ATTRIB_GENERAL_MAX_EDIT) );
ASSERT( GetDlgItem(IDC_ATTRIB_GENERAL_DEACTIVATE) );
reinterpret_cast<CEdit *>( GetDlgItem(IDC_ATTRIB_GENERAL_MIN_EDIT) )->SetReadOnly();
reinterpret_cast<CEdit *>( GetDlgItem(IDC_ATTRIB_GENERAL_MAX_EDIT) )->SetReadOnly();
GetDlgItem(IDC_ATTRIB_GENERAL_DEACTIVATE)->EnableWindow( FALSE );
}
hr = DissableReadOnlyAttributes( this, pIADsObject, ctrls, sizeof(ctrls)/sizeof(ctrls[0]) );
ASSERT( SUCCEEDED(hr) ); // shouldn't fail, but unimportant, so ignore error
// warn the user if this is a read/write defunct object
ASSERT( GetDlgItem(IDC_ATTRIB_GENERAL_DEACTIVATE) );
if( DDXDefunct &&
GetDlgItem(IDC_ATTRIB_GENERAL_DEACTIVATE)->IsWindowEnabled() )
{
AfxMessageBox( IDS_WARNING_DEFUNCT, MB_OK | MB_ICONINFORMATION );
}
//
// Remember that the data is loaded.
//
fDataLoaded = TRUE;
m_editLowerRange.SetSigned( g_Syntax[ SyntaxOrdinal ].m_fIsSigned );
m_editUpperRange.SetSigned( g_Syntax[ SyntaxOrdinal ].m_fIsSigned );
}
//
// Set up the dialog data exchange.
//
DDX_Text( pDX, IDC_ATTRIB_GENERAL_NAME_STATIC, ObjectName );
DDX_Text( pDX, IDC_ATTRIB_GENERAL_SYSCLASS_STATIC, SysClassString );
DDX_Text( pDX, IDC_ATTRIB_GENERAL_SYNTAX_EDIT, SyntaxString );
DDX_Text( pDX, IDC_ATTRIB_GENERAL_OID_EDIT, OidString );
DDX_Text( pDX, IDC_ATTRIB_GENERAL_VALUE_STATIC, MultiValued );
DDX_Text( pDX, IDC_ATTRIB_GENERAL_LDN, DisplayName );
DDX_Text( pDX, IDC_ATTRIB_GENERAL_DESCRIPTION_EDIT, DDXDescription );
DDXV_VerifyAttribRange( pDX, g_Syntax[ pObject->SyntaxOrdinal ].m_fIsSigned,
IDC_ATTRIB_GENERAL_MIN_EDIT, DDXRangeLower,
IDC_ATTRIB_GENERAL_MAX_EDIT, DDXRangeUpper );
DDX_Check( pDX, IDC_ATTRIB_GENERAL_DISPLAYABLE_CHECK, DDXDisplayable );
DDX_Check( pDX, IDC_ATTRIB_GENERAL_INDEX_CHECK, DDXIndexed );
DDX_Check( pDX, IDC_ATTRIB_GENERAL_ANR_CHECK, DDXANR );
DDX_Check( pDX, IDC_ATTRIB_GENERAL_CPYATTR_CHECK, DDXCopyOnDuplicate );
DDX_Check( pDX, IDC_ATTRIB_GENERAL_REPLICATED, DDXReplicatedToGC );
DDX_Check( pDX, IDC_ATTRIB_GENERAL_CONTAINERIZED_INDEX_CHECK, DDXContainerIndexed );
// Since we want the checkbox label to be positive
// the value is actually the opposite of defunct
int checkValue = !Defunct;
DDX_Check( pDX, IDC_ATTRIB_GENERAL_DEACTIVATE, checkValue );
DDXDefunct = !checkValue;
return;
}
void
AttributeGeneralPage::OnIndexClick()
{
ASSERT( pObject );
if( pObject && g_Syntax[ pObject->SyntaxOrdinal ].m_fIsANRCapable )
{
GetDlgItem(IDC_ATTRIB_GENERAL_ANR_CHECK)->
EnableWindow( IsDlgButtonChecked(IDC_ATTRIB_GENERAL_INDEX_CHECK) );
}
}
void
AttributeGeneralPage::OnDeactivateClick()
{
if( !IsDlgButtonChecked(IDC_ATTRIB_GENERAL_DEACTIVATE) )
{
if( IDOK != AfxMessageBox( IDS_WARNING_DEFUNCT_SET, MB_OKCANCEL | MB_ICONWARNING ) )
{
CheckDlgButton( IDC_ATTRIB_GENERAL_DEACTIVATE, BST_UNCHECKED );
}
}
}
// Search User class & aux classes for the specified attribute
BOOL
AttributeGeneralPage::IsAttributeInUserClass( const CString & strAttribDN )
{
BOOL fFound = FALSE;
ComponentData & Scope = pComponent->QueryComponentDataRef();
SchemaObject * pObject = Scope.g_SchemaCache.LookupSchemaObject(
CString( szUserClass ),
SCHMMGMT_CLASS );
//
// Call the attribute check routine. This routine
// will call itself recursively to search the
// inheritance structure of the class User.
//
if ( pObject ) {
fFound = RecursiveIsAttributeInUserClass( strAttribDN, pObject );
Scope.g_SchemaCache.ReleaseRef( pObject );
}
return fFound ;
}
// Search the user class & subclasses
BOOL
AttributeGeneralPage::RecursiveIsAttributeInUserClass(
const CString & strAttribDN,
SchemaObject * pObject )
{
BOOL fFound = FALSE;
//
// Don't process "top" here since everyone inherits from it.
//
// i don't think we ever get "top" here?
ASSERT( pObject->ldapDisplayName.CompareNoCase( szTopClass ) );
if ( !pObject->ldapDisplayName.CompareNoCase(szTopClass) )
return fFound;
DebugTrace( L"RecursiveIsAttributeInUserClass: %ls\n",
const_cast<LPWSTR>((LPCTSTR)pObject->ldapDisplayName) );
// Check every list
if( !SearchResultList( strAttribDN, pObject->systemMayContain) &&
!SearchResultList( strAttribDN, pObject->mayContain) &&
!SearchResultList( strAttribDN, pObject->systemMustContain) &&
!SearchResultList( strAttribDN, pObject->mustContain) )
{
//
// The attribute was not found in the given class, diging deeper...
// Check each auxiliary class...
//
fFound = TraverseAuxiliaryClassList( strAttribDN,
pObject->systemAuxiliaryClass );
if( !fFound )
{
fFound = TraverseAuxiliaryClassList( strAttribDN,
pObject->auxiliaryClass );
}
}
else
{
fFound = TRUE;
}
return fFound ;
}
// Linear search of the linked list for the string strAttribDN
BOOL
AttributeGeneralPage::SearchResultList(
const CString & strAttribDN,
ListEntry * pList )
{
// Traverse the list
while ( pList )
{
// Searching for the existance of the attribute
if( !pList->Attribute.CompareNoCase( strAttribDN ) )
return TRUE;
pList = pList->pNext;
}
return FALSE;
}
// Traverse each auxiliary class by recursivly
// calling RecursiveIsAttributeInUserClass()
BOOL
AttributeGeneralPage::TraverseAuxiliaryClassList(
const CString & strAttribDN,
ListEntry * pList )
{
SchemaObject * pInheritFrom = NULL;
ComponentData & Scope = pComponent->QueryComponentDataRef();
BOOL fFound = FALSE;
while ( !fFound && pList ) {
pInheritFrom = Scope.g_SchemaCache.LookupSchemaObject( pList->Attribute,
SCHMMGMT_CLASS );
if ( pInheritFrom )
{
// recursive call
fFound = RecursiveIsAttributeInUserClass( strAttribDN, pInheritFrom );
Scope.g_SchemaCache.ReleaseRef( pInheritFrom );
}
pList = pList->pNext;
}
return fFound ;
}
HRESULT
AttributeGeneralPage::ChangeDefunctState( BOOL DDXDefunct,
BOOL & Defunct,
IADsPropertyList * pPropertyList,
BOOL & fApplyAbort,
BOOL & fApplyFailed )
{
ASSERT( !fApplyAbort && DDXDefunct != Defunct );
VARIANT AdsValue;
HRESULT hr = S_OK;
VariantInit( &AdsValue );
V_VT(&AdsValue) = VT_BOOL;
if ( DDXDefunct ) {
V_BOOL(&AdsValue) = -1;
} else {
V_BOOL(&AdsValue) = 0;
}
hr = pIADsObject->Put( const_cast<BSTR>((LPCTSTR)g_isDefunct),
AdsValue );
ASSERT( SUCCEEDED( hr ) );
hr = pIADsObject->SetInfo();
if ( FAILED( hr ) ) {
pPropertyList->PurgePropertyList();
if( ERROR_DS_UNWILLING_TO_PERFORM == HRESULT_CODE(hr) )
{
fApplyFailed = TRUE;
DoErrMsgBox( ::GetActiveWindow(),
TRUE,
DDXDefunct ? IDS_ERR_EDIT_DEFUNCT_SET : IDS_ERR_EDIT_DEFUNCT_REMOVE );
}
else
{
fApplyAbort = TRUE;
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
}
} else {
pObject->isDefunct = DDXDefunct;
Defunct = DDXDefunct;
}
return hr;
}