#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( 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(&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((LPCTSTR)DDXDescription); if ( DDXDescription.IsEmpty() ) { hr = pIADsObject->PutEx( ADS_PROPERTY_CLEAR, const_cast((LPCTSTR)g_Description), AdsValue ); } else { hr = pIADsObject->Put( const_cast((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((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((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((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((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((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((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(&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((LPCTSTR)g_DisplayName), &AdsResult ); if ( SUCCEEDED( hr ) ) { ASSERT( AdsResult.vt == VT_BSTR ); ObjectName = AdsResult.bstrVal; VariantClear( &AdsResult ); } // // Description // hr = pIADsObject->Get( const_cast((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((LPCTSTR)g_CN), &AdsResult ); if ( SUCCEEDED( hr ) ) { ASSERT( AdsResult.vt == VT_BSTR ); DisplayName = AdsResult.bstrVal; VariantClear( &AdsResult ); } // // SysClass // hr = pIADsObject->Get( const_cast((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((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((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((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((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((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((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((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( GetDlgItem(IDC_ATTRIB_GENERAL_MIN_EDIT) )->SetReadOnly(); reinterpret_cast( 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((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((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; }