/**** SchmUtil.cpp Various common utility routines for the Schema Editor Snap-In. ****/ #include "stdafx.h" #include "macros.h" USE_HANDLE_MACROS("SCHMMGMT(schmutil.cpp)") #include "resource.h" #include "cache.h" #include "schmutil.h" #include "compdata.h" #include // CryptEncodeObject() and CryptDecodeObject() // // Removed from public headers by DS guys // See bug 454342 XOM will not survive the transition to Win64 // //#include // // Global strings for classes and attributes in the DS. // These are NOT subject to localization. // LPWSTR g_DisplayName = L"ldapDisplayName"; LPWSTR g_ClassFilter = L"classSchema"; LPWSTR g_AttributeFilter = L"attributeSchema"; LPWSTR g_Description = L"adminDescription"; LPWSTR g_MayContain = L"mayContain"; LPWSTR g_MustContain = L"mustContain"; LPWSTR g_SystemMayContain = L"systemMayContain"; LPWSTR g_SystemMustContain = L"systemMustContain"; LPWSTR g_AuxiliaryClass = L"auxiliaryClass"; LPWSTR g_SystemAuxiliaryClass = L"systemAuxiliaryClass"; LPWSTR g_SubclassOf = L"subclassOf"; LPWSTR g_ObjectClassCategory = L"objectClassCategory"; LPWSTR g_ObjectClass = L"objectClass"; LPWSTR g_omObjectClass = L"oMObjectClass"; LPWSTR g_CN = L"CN"; LPWSTR g_omSyntax = L"oMSyntax"; LPWSTR g_AttributeSyntax = L"attributeSyntax"; LPWSTR g_SystemOnly = L"systemOnly"; LPWSTR g_Superiors = L"possSuperiors"; LPWSTR g_SystemSuperiors = L"systemPossSuperiors"; LPWSTR g_GlobalClassID = L"governsID"; LPWSTR g_GlobalAttributeID = L"attributeID"; LPWSTR g_RangeUpper = L"rangeUpper"; LPWSTR g_RangeLower = L"rangeLower"; LPWSTR g_IsSingleValued = L"isSingleValued"; LPWSTR g_IndexFlag = L"searchFlags"; LPWSTR g_ShowInAdvViewOnly = L"showInAdvancedViewOnly"; LPWSTR g_UpdateSchema = LDAP_OPATT_SCHEMA_UPDATE_NOW_W; LPWSTR g_BecomeFsmo = LDAP_OPATT_BECOME_SCHEMA_MASTER_W; LPWSTR g_isDefunct = L"isDefunct"; LPWSTR g_GCReplicated = L"isMemberOfPartialAttributeSet"; LPWSTR g_DefaultAcl = L"defaultSecurityDescriptor"; LPWSTR g_DefaultCategory = L"defaultObjectCategory"; LPWSTR g_systemFlags = L"systemFlags"; LPWSTR g_fsmoRoleOwner = L"fsmoRoleOwner"; LPWSTR g_allowedChildClassesEffective = L"allowedChildClassesEffective"; LPWSTR g_allowedAttributesEffective = L"allowedAttributesEffective"; LPWSTR g_ClassSearchRequest = L"objectClass=classSchema"; LPWSTR g_AttribSearchRequest = L"objectClass=attributeSchema"; // // Syntax values. Not subject to localization. // class CSyntaxDescriptor g_Syntax[] = { // NTRAID#NTBUG9-540278-2002/05/15-lucios // Added SYNTAX_CASE_STRING_TYPE // This list should be kept in alphabetical order since the combo box is not sorted // so that the index from the combo box can map to an entry in this table // nResourceID, fIsSigned, fIsANRCapable, pszAttributeSyntax, nOmSyntax, dwOmObjectClass, pOmObjectClass /*SYNTAX_DISTNAME_STRING_TYPE (Access-Point) */ CSyntaxDescriptor( IDS_SYNTAX_ACCESS_POINT,FALSE, FALSE, _T("2.5.5.14"), /*OM_S_OBJECT */ 127, 9, (LPBYTE)"\x2B\x0C\x02\x87\x73\x1C\x00\x85\x3E" ), /*SYNTAX_ADDRESS_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_ADDRESS, FALSE, FALSE, _T("2.5.5.13"), /*OM_S_OBJECT */ 127, 9, (LPBYTE)"\x2B\x0C\x02\x87\x73\x1C\x00\x85\x5C" ), /*SYNTAX_BOOLEAN_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_BOOLEAN, FALSE, FALSE, _T("2.5.5.8") , /*OM_S_BOOLEAN */ 1 , 0, NULL ), /*SYNTAX_NOCASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NOCASE_STR, FALSE, TRUE, _T("2.5.5.4") , /*OM_S_TELETEX_STRING */ 20 , 0, NULL ), /*SYNTAX_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_CASE_STR, FALSE, TRUE , _T("2.5.5.3"), /*OM_S_GENERAL_STRING */ 27 , 0, NULL ), /*SYNTAX_DISTNAME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_DN, FALSE, FALSE, _T("2.5.5.1") , /*OM_S_OBJECT */ 127, 9, (LPBYTE)"\x2B\x0C\x02\x87\x73\x1C\x00\x85\x4A" ), /*SYNTAX_DISTNAME_STRING_TYPE (DN-String) */ CSyntaxDescriptor( IDS_SYNTAX_DNSTRING, FALSE, FALSE, _T("2.5.5.14"), /*OM_S_OBJECT */ 127, 10, (LPBYTE)"\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0C" ), /*SYNTAX_DISTNAME_BINARY_TYPE (DN-Binary) */ CSyntaxDescriptor( IDS_SYNTAX_DN_BINARY, FALSE, FALSE, _T("2.5.5.7") , /*OM_S_OBJECT */ 127, 10, (LPBYTE)"\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x0B" ), /*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_ENUMERATION, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_ENUMERATION */ 10 , 0, NULL ), /*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_GEN_TIME, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_GENERALISED_TIME_STRING */ 24 , 0, NULL ), /*SYNTAX_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_I5_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_IA5_STRING */ 22 , 0, NULL ), /*SYNTAX_INTEGER_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_INTEGER, TRUE, FALSE, _T("2.5.5.9") , /*OM_S_INTEGER */ 2 , 0, NULL ), /*SYNTAX_I8_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_LINT, FALSE, FALSE, _T("2.5.5.16"), /*OM_S_I8 */ 65 , 0, NULL ), /*SYNTAX_NT_SECURITY_DESCRIPTOR_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_SEC_DESC, FALSE, FALSE, _T("2.5.5.15"), /*OM_S_OBJECT_SECURITY_DESCRIPTOR*/ 66 , 0, NULL ), /*SYNTAX_NUMERIC_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_NUMSTR, FALSE, FALSE, _T("2.5.5.6") , /*OM_S_NUMERIC_STRING */ 18 , 0, NULL ), /*SYNTAX_OBJECT_ID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OID, FALSE, FALSE, _T("2.5.5.2") , /*OM_S_OBJECT_IDENTIFIER_STRING */ 6 , 0, NULL ), /*SYNTAX_OCTET_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_OCTET, FALSE, FALSE, _T("2.5.5.10"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ), /*SYNTAX_DISTNAME_BINARY_TYPE (OR-Name) */ CSyntaxDescriptor( IDS_SYNTAX_OR_NAME, FALSE, FALSE, _T("2.5.5.7") , /*OM_S_OBJECT */ 127, 7, (LPBYTE)"\x56\x06\x01\x02\x05\x0B\x1D" ), /*SYNTAX_PRINT_CASE_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_PRCS_STR, FALSE, TRUE, _T("2.5.5.5") , /*OM_S_PRINTABLE_STRING */ 19 , 0, NULL ), /*SYNTAX_OCTET_STRING_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_REPLICA_LINK,FALSE, FALSE, _T("2.5.5.10"), /*OM_S_OBJECT */ 127, 10, (LPBYTE)"\x2A\x86\x48\x86\xF7\x14\x01\x01\x01\x06" ), /*SYNTAX_SID_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_SID, FALSE, FALSE, _T("2.5.5.17"), /*OM_S_OCTET_STRING */ 4 , 0, NULL ), /*SYNTAX_UNICODE_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UNICODE, FALSE, TRUE, _T("2.5.5.12"), /*OM_S_UNICODE_STRING */ 64 , 0, NULL ), /*SYNTAX_TIME_TYPE */ CSyntaxDescriptor( IDS_SYNTAX_UTC, FALSE, FALSE, _T("2.5.5.11"), /*OM_S_UTC_TIME_STRING */ 23 , 0, NULL ), /* *** unknown -- must be last *** */ CSyntaxDescriptor( IDS_SYNTAX_UNKNOWN, FALSE, TRUE, NULL, 0 , 0, NULL ), }; const UINT SCHEMA_SYNTAX_UNKNOWN = sizeof( g_Syntax ) / sizeof( g_Syntax[0] ) - 1; // Number formating printf strings const LPWSTR g_UINT32_FORMAT = L"%u"; #ifdef ENABLE_NEGATIVE_INT const LPWSTR g_INT32_FORMAT = L"%d"; #else // if there is no negative numbers support, format as unsigned const LPWSTR g_INT32_FORMAT = g_UINT32_FORMAT; #endif // // ******************************************************************* // These are loaded from the resources as they need to be localizable. // ******************************************************************* // // // Global strings for our static nodes. // CString g_strSchmMgmt; CString g_strClasses; CString g_strAttributes; // // Strings for various object types. // CString g_88Class; CString g_StructuralClass; CString g_AuxClass; CString g_AbstractClass; CString g_MandatoryAttribute; CString g_OptionalAttribute; CString g_Yes; CString g_No; CString g_Unknown; CString g_Defunct; CString g_Active; // // Message strings. // CString g_NoDescription; CString g_NoName; CString g_MsgBoxErr; CString g_MsgBoxWarn; // // Menu strings. // CString g_MenuStrings[MENU_LAST_COMMAND]; CString g_StatusStrings[MENU_LAST_COMMAND]; BOOL g_fScopeStringsLoaded = FALSE; // // Utility functions. // void LoadGlobalCookieStrings( ) /*** Load the global strings out of our resource table. ***/ { if ( !g_fScopeStringsLoaded ) { // // Static node strings. // VERIFY( g_strSchmMgmt.LoadString(IDS_SCOPE_SCHMMGMT) ); VERIFY( g_strClasses.LoadString(IDS_SCOPE_CLASSES) ); VERIFY( g_strAttributes.LoadString(IDS_SCOPE_ATTRIBUTES) ); // // Object name strings. // VERIFY( g_88Class.LoadString(IDS_CLASS_88) ); VERIFY( g_StructuralClass.LoadString(IDS_CLASS_STRUCTURAL) ); VERIFY( g_AuxClass.LoadString(IDS_CLASS_AUXILIARY) ); VERIFY( g_AbstractClass.LoadString(IDS_CLASS_ABSTRACT) ); VERIFY( g_MandatoryAttribute.LoadString(IDS_ATTRIBUTE_MANDATORY) ); VERIFY( g_OptionalAttribute.LoadString(IDS_ATTRIBUTE_OPTIONAL) ); VERIFY( g_Yes.LoadString(IDS_YES) ); VERIFY( g_No.LoadString(IDS_NO) ); VERIFY( g_Unknown.LoadString(IDS_UNKNOWN) ); VERIFY( g_Defunct.LoadString(IDS_DEFUNCT) ); VERIFY( g_Active.LoadString(IDS_ACTIVE) ); // // Message strings. // VERIFY( g_NoDescription.LoadString(IDS_ERR_NO_DESCRIPTION) ); VERIFY( g_NoName.LoadString(IDS_ERR_NO_NAME) ); VERIFY( g_MsgBoxErr.LoadString(IDS_ERR_ERROR) ); VERIFY( g_MsgBoxWarn.LoadString(IDS_ERR_WARNING) ); // // Syntax strings. // for( UINT i = 0; i <= SCHEMA_SYNTAX_UNKNOWN; i++ ) { ASSERT( g_Syntax[i].m_nResourceID ); VERIFY( g_Syntax[i].m_strSyntaxName.LoadString( g_Syntax[i].m_nResourceID ) ); } // // Menu Strings // VERIFY( g_MenuStrings[CLASSES_CREATE_CLASS].LoadString(IDS_MENU_CLASS) ); VERIFY ( g_MenuStrings[VIEW_DEFUNCT_OBJECTS].LoadString ( IDS_MENU_VIEW_DEFUNCT_OBJECTS ) ); VERIFY( g_MenuStrings[NEW_CLASS].LoadString(IDS_MENU_NEW_CLASS) ); VERIFY( g_MenuStrings[ATTRIBUTES_CREATE_ATTRIBUTE].LoadString( IDS_MENU_ATTRIBUTE) ); VERIFY(g_MenuStrings[NEW_ATTRIBUTE].LoadString(IDS_MENU_NEW_ATTRIBUTE)); VERIFY( g_MenuStrings[SCHEMA_RETARGET].LoadString(IDS_MENU_RETARGET) ); VERIFY( g_MenuStrings[SCHEMA_EDIT_FSMO].LoadString(IDS_MENU_EDIT_FSMO) ); VERIFY( g_MenuStrings[SCHEMA_REFRESH].LoadString(IDS_MENU_REFRESH) ); VERIFY( g_MenuStrings[SCHEMA_SECURITY].LoadString(IDS_MENU_SECURITY) ); VERIFY( g_StatusStrings[CLASSES_CREATE_CLASS].LoadString( IDS_STATUS_CREATE_CLASS) ); VERIFY ( g_StatusStrings[VIEW_DEFUNCT_OBJECTS].LoadString ( IDS_STATUS_VIEW_DEFUNCT_OBJECTS ) ); VERIFY( g_StatusStrings[ATTRIBUTES_CREATE_ATTRIBUTE].LoadString( IDS_STATUS_CREATE_ATTRIBUTE) ); VERIFY( g_StatusStrings[NEW_CLASS].LoadString( IDS_STATUS_CREATE_CLASS) ); VERIFY( g_StatusStrings[NEW_ATTRIBUTE].LoadString( IDS_STATUS_CREATE_ATTRIBUTE) ); VERIFY( g_StatusStrings[SCHEMA_RETARGET].LoadString(IDS_STATUS_RETARGET) ); VERIFY( g_StatusStrings[SCHEMA_EDIT_FSMO].LoadString(IDS_STATUS_EDIT_FSMO) ); VERIFY( g_StatusStrings[SCHEMA_REFRESH].LoadString(IDS_STATUS_REFRESH) ); VERIFY( g_StatusStrings[SCHEMA_SECURITY].LoadString(IDS_STATUS_SECURITY) ); g_fScopeStringsLoaded = TRUE; } } INT DoErrMsgBox( HWND hwndParent, // IN: Parent of the dialog box BOOL fError, // IN: Is this a warning or an error? UINT wIdString, // IN: String resource Id of the error. HRESULT hr // IN: the error code (optional) ) /*** Display a message box with the error. ***/ { CString Error; VERIFY( Error.LoadString( wIdString ) ); return DoErrMsgBox( hwndParent, fError, Error, hr ); } INT DoErrMsgBox( HWND hwndParent, // IN: Parent of the dialog box BOOL fError, // IN: Is this a warning or an error? PCWSTR pszError, // IN: String to display. HRESULT hr // IN: the error code (optional) ) /*** Display a message box with the error. ***/ { CThemeContextActivator activator; PTSTR ptzSysMsg = NULL; int cch = 0; if (FAILED(hr)) { cch = cchLoadHrMsg(hr, &ptzSysMsg, TRUE); } if (!cch) { return MessageBox( hwndParent, pszError, (fError ? g_MsgBoxErr : g_MsgBoxWarn), (fError ? MB_ICONSTOP : MB_ICONEXCLAMATION) | MB_OK ); } CString szError = pszError; szError += L"\n"; szError += ptzSysMsg; return MessageBox( hwndParent, szError, (fError ? g_MsgBoxErr : g_MsgBoxWarn), (fError ? MB_ICONSTOP : MB_ICONEXCLAMATION) | MB_OK ); } HRESULT ComponentData::ForceDsSchemaCacheUpdate( VOID ) /*** Force the schema container to reload its interal cache. If this succeeds, it returns TRUE. Otherwise, it returns FALSE. ***/ { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); CWaitCursor wait; CString RootDsePath; IADs *pSchemaRootDse = NULL; SAFEARRAYBOUND RootDseBoundary[1]; SAFEARRAY* pSafeArray = NULL; VARIANT AdsArray, AdsValue; long ArrayLen = 1; long ArrayPos = 0; HRESULT hr = S_OK; do { // // Open the root DSE on the current focus server. // GetBasePathsInfo()->GetRootDSEPath(RootDsePath); // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR // NOTE: const_cast not necessary here since ADsGetObject takes a WSTR for the first parameter. hr = SchemaOpenObject( ( const_cast((LPCTSTR) RootDsePath ) ), IID_IADs, (void **)&pSchemaRootDse ); BREAK_ON_FAILED_HRESULT(hr); // // Create the safe array for the PutEx call. // RootDseBoundary[0].lLbound = 0; RootDseBoundary[0].cElements = ArrayLen; pSafeArray = SafeArrayCreate( VT_VARIANT, ArrayLen, RootDseBoundary ); BREAK_ON_FAILED_HRESULT(hr); VariantInit( &AdsArray ); V_VT( &AdsArray ) = VT_ARRAY | VT_VARIANT; V_ARRAY( &AdsArray ) = pSafeArray; VariantInit( &AdsValue ); V_VT(&AdsValue) = VT_I4; V_I4(&AdsValue) = 1; hr = SafeArrayPutElement( pSafeArray, &ArrayPos, &AdsValue ); BREAK_ON_FAILED_HRESULT(hr); // // Write the update parameter. This is synchronous // and when it returns, the cache is up to date. // // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = pSchemaRootDse->PutEx( ADS_PROPERTY_APPEND, const_cast((LPCTSTR)g_UpdateSchema), AdsArray ); if(FAILED(hr)) {hr=S_FALSE;break;} // schema is read only hr = pSchemaRootDse->SetInfo(); if(FAILED(hr)) {hr=S_FALSE;break;} // schema is read only } while( FALSE ); SafeArrayDestroy( pSafeArray ); if( pSchemaRootDse ) pSchemaRootDse->Release(); return hr; } BOOLEAN ComponentData::AsynchForceDsSchemaCacheUpdate( VOID ) { AFX_MANAGE_STATE(AfxGetStaticModuleState( )); CWaitCursor wait; CString szSchemaContainerPath; IADs *pSchemaContainer; VARIANT AdsValue; HRESULT hr; SYSTEMTIME CurrentTime; double variant_time; // // Get the schema container path. // GetBasePathsInfo()->GetSchemaPath(szSchemaContainerPath); if (szSchemaContainerPath.IsEmpty() ) { return FALSE; } // // Open the schema container. // hr = SchemaOpenObject( (LPWSTR)(LPCWSTR)szSchemaContainerPath, IID_IADs, (void **)&pSchemaContainer ); if ( FAILED(hr) ) { return FALSE; } // // Write the update parameter. // GetSystemTime( &CurrentTime ); BOOL result = SystemTimeToVariantTime( &CurrentTime, &variant_time ); ASSERT( result ); VariantInit( &AdsValue ); V_VT(&AdsValue) = VT_DATE; V_DATE(&AdsValue) = variant_time; // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = pSchemaContainer->Put( const_cast((LPCTSTR)g_UpdateSchema), AdsValue ); // NTRAID#NTBUG9-542354-2002/02/14-dantra-Errors returned by IADs::Put and PutEx are being masked. if ( SUCCEEDED( hr ) ) hr = pSchemaContainer->SetInfo(); pSchemaContainer->Release(); if ( FAILED( hr ) ) { return FALSE; } return TRUE; } HRESULT InsertEditItems( HWND hwnd, VARIANT *AdsResult ) { HRESULT hr; SAFEARRAY *saAttributes; long start, end, current; VARIANT SingleResult; // // Check the VARIANT to make sure we have // an array of variants. // ASSERT( V_VT(AdsResult) == ( VT_ARRAY | VT_VARIANT ) ); saAttributes = V_ARRAY( AdsResult ); // // Figure out the dimensions of the array. // hr = SafeArrayGetLBound( saAttributes, 1, &start ); if ( FAILED(hr) ) { return S_FALSE; } hr = SafeArrayGetUBound( saAttributes, 1, &end ); if ( FAILED(hr) ) { return S_FALSE; } VariantInit( &SingleResult ); // // Process the array elements. // for ( current = start ; current <= end ; current++ ) { hr = SafeArrayGetElement( saAttributes, ¤t, &SingleResult ); if ( SUCCEEDED( hr ) ) { ASSERT( V_VT(&SingleResult) == VT_BSTR ); ::SendMessage( hwnd, LB_ADDSTRING, 0, reinterpret_cast(V_BSTR(&SingleResult)) ); ::SendMessage( hwnd, LB_SETITEMDATA, 0, NULL ); VariantClear( &SingleResult ); } } return S_OK; } HRESULT InsertEditItems( CListBox& refListBox, CStringList& refstringlist ) { POSITION pos = refstringlist.GetHeadPosition(); while (pos != NULL) { int iItem = refListBox.AddString( refstringlist.GetNext(pos) ); if (0 > iItem) { ASSERT(FALSE); return E_OUTOFMEMORY; } else { VERIFY( LB_ERR != refListBox.SetItemDataPtr( iItem, NULL ) ); } } return S_OK; } inline BOOL IsEqual( ADS_OCTET_STRING * ostr1, ADS_OCTET_STRING * ostr2 ) { ASSERT(ostr1); ASSERT(ostr2); if( ostr1->dwLength == ostr2->dwLength ) { if( 0 == ostr1->dwLength ) return TRUE; else return !memcmp( ostr1->lpValue, ostr2->lpValue, ostr1->dwLength ); } else return FALSE; } UINT GetSyntaxOrdinal( PCTSTR attributeSyntax, UINT omSyntax, ADS_OCTET_STRING * pOmObjectClass ) { ASSERT( attributeSyntax ); ASSERT( omSyntax ); ASSERT( pOmObjectClass ); // // Return the syntax ordinal, or the unknown syntax ordinal. // UINT Ordinal = 0; while ( Ordinal < SCHEMA_SYNTAX_UNKNOWN) { if ( !_tcscmp(g_Syntax[Ordinal].m_pszAttributeSyntax, attributeSyntax)) { if( omSyntax && g_Syntax[Ordinal].m_nOmSyntax == omSyntax && IsEqual( &g_Syntax[Ordinal].m_octstrOmObjectClass, pOmObjectClass ) ) break; } Ordinal++; } return Ordinal; } // Coded to fail on anything suspicious HRESULT VariantToStringList( VARIANT& refvar, CStringList& refstringlist ) { HRESULT hr = S_OK; long start, end, current; // // Check the VARIANT to make sure we have // an array of variants. // if ( V_VT(&refvar) != ( VT_ARRAY | VT_VARIANT ) ) { ASSERT(FALSE); return E_UNEXPECTED; } SAFEARRAY *saAttributes = V_ARRAY( &refvar ); // // Figure out the dimensions of the array. // hr = SafeArrayGetLBound( saAttributes, 1, &start ); if( FAILED(hr) ) return hr; hr = SafeArrayGetUBound( saAttributes, 1, &end ); if( FAILED(hr) ) return hr; VARIANT SingleResult; VariantInit( &SingleResult ); // // Process the array elements. // for ( current = start ; current <= end ; current++ ) { hr = SafeArrayGetElement( saAttributes, ¤t, &SingleResult ); if( FAILED(hr) ) return hr; if ( V_VT(&SingleResult) != VT_BSTR ) return E_UNEXPECTED; refstringlist.AddHead( V_BSTR(&SingleResult) ); VariantClear( &SingleResult ); } return S_OK; } HRESULT StringListToVariant( VARIANT& refvar, CStringList& refstringlist ) { HRESULT hr = S_OK; int cCount = (int) refstringlist.GetCount(); SAFEARRAYBOUND rgsabound[1]; rgsabound[0].lLbound = 0; rgsabound[0].cElements = cCount; SAFEARRAY* psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound); if (NULL == psa) return E_OUTOFMEMORY; VariantClear( &refvar ); V_VT(&refvar) = VT_VARIANT|VT_ARRAY; V_ARRAY(&refvar) = psa; VARIANT SingleResult; VariantInit( &SingleResult ); V_VT(&SingleResult) = VT_BSTR; POSITION pos = refstringlist.GetHeadPosition(); long i; for (i = 0; i < cCount, pos != NULL; i++) { V_BSTR(&SingleResult) = T2BSTR((LPCTSTR)refstringlist.GetNext(pos)); hr = SafeArrayPutElement(psa, &i, &SingleResult); if( FAILED(hr) ) return hr; } if (i != cCount || pos != NULL) return E_UNEXPECTED; return hr; } HRESULT StringListToColumnList( ComponentData* pScopeControl, CStringList& refstringlist, ListEntry **ppNewList ) { // // Make a column list from a string list. We use // this to update the cached attributes lists. // int cCount = (int) refstringlist.GetCount(); ListEntry *pHead = NULL; ListEntry *pCurrent = NULL, *pPrevious = NULL; POSITION pos = refstringlist.GetHeadPosition(); CString Name; SchemaObject *pSchemaObject, *pSchemaHead; BOOLEAN fNameFound; for ( long i = 0; i < cCount, pos != NULL; i++ ) { pCurrent = new ListEntry; if ( !pCurrent ) { pScopeControl->g_SchemaCache.FreeColumnList( pHead ); return E_OUTOFMEMORY; } if ( !pHead ) { pHead = pPrevious = pCurrent; } else { pPrevious->pNext = pCurrent; pPrevious = pCurrent; } // // We need to list all of these by their ldapDisplayNames, // so we have to reverse lookup the oid entries. // Name = ((LPCTSTR)refstringlist.GetNext(pos)); pSchemaObject = pScopeControl->g_SchemaCache.LookupSchemaObject( Name, SCHMMGMT_CLASS ); if ( !pSchemaObject ) { pSchemaObject = pScopeControl->g_SchemaCache.LookupSchemaObject( Name, SCHMMGMT_ATTRIBUTE ); if ( !pSchemaObject) { // // We have to look up this oid. // First try the list of classes. // pSchemaHead = pScopeControl->g_SchemaCache.pSortedClasses; pSchemaObject = pSchemaHead; fNameFound = FALSE; do { if ( pSchemaObject->oid == Name ) { Name = pSchemaObject->ldapDisplayName; fNameFound = TRUE; break; } pSchemaObject = pSchemaObject->pSortedListFlink; } while ( pSchemaObject != pSchemaHead ); // // Then try the list of attributes. // if ( !fNameFound ) { pSchemaHead = pScopeControl->g_SchemaCache.pSortedAttribs; pSchemaObject = pSchemaHead; do { if ( pSchemaObject->oid == Name ) { Name = pSchemaObject->ldapDisplayName; fNameFound = TRUE; break; } pSchemaObject = pSchemaObject->pSortedListFlink; } while ( pSchemaObject != pSchemaHead ); } ASSERT( fNameFound ); } else { pScopeControl->g_SchemaCache.ReleaseRef( pSchemaObject ); } } else { pScopeControl->g_SchemaCache.ReleaseRef( pSchemaObject ); } // // This is the ldapDisplayName!! // pCurrent->Attribute = Name; } ASSERT( cCount == i ); ASSERT( pos == NULL ); *ppNewList = pHead; return S_OK; } const UINT MAX_ERROR_BUF = 2048; VOID DoExtErrMsgBox( VOID ) { CThemeContextActivator activator; DWORD dwLastError; WCHAR szErrorBuf[MAX_ERROR_BUF + 1]; WCHAR szNameBuf[MAX_ERROR_BUF + 1]; // get extended error value HRESULT hr_return = ADsGetLastError( &dwLastError, szErrorBuf, MAX_ERROR_BUF, szNameBuf, MAX_ERROR_BUF); if (SUCCEEDED(hr_return)) { MessageBox( ::GetActiveWindow(), szErrorBuf, szNameBuf, MB_OK | MB_ICONSTOP ); } else ASSERT( FALSE ); } // INVALID_POINTER is returned by CListBox::GetItemDataPtr() in case of an error. const VOID * INVALID_POINTER = reinterpret_cast( LB_ERR ); HRESULT RetrieveEditItemsWithExclusions( CListBox& refListBox, CStringList& refstringlist, CStringList* pstringlistExclusions) { CString str; CString * pstr = NULL; int nCount = refListBox.GetCount(); if (LB_ERR == nCount) { ASSERT(FALSE); return E_UNEXPECTED; } for (INT i = 0; i < nCount; i++) { pstr = static_cast( refListBox.GetItemDataPtr(i) ); ASSERT( INVALID_POINTER != pstr ); // don't need to search for pstr because pstr can only be a new item, // and they are never excluded. if( pstr && INVALID_POINTER != pstr ) { refstringlist.AddHead( *pstr ); } else { refListBox.GetText( i, str ); if (NULL != pstringlistExclusions) { POSITION pos = pstringlistExclusions->Find( str ); if (NULL != pos) continue; } refstringlist.AddHead( str ); } } return S_OK; } // // The global cookie lists for scope and result pane items. // VOID CCookieList::AddCookie( Cookie *pCookie, HSCOPEITEM hScope ) { CCookieListEntry *pNewEntry = new CCookieListEntry; // // If there's no memory, we can't remember this and hence // our display may get a little out of whack. // if ( !pNewEntry ) { return; } pNewEntry->pCookie = pCookie; pNewEntry->hScopeItem = hScope; if ( !pHead ) { // // If this is the first one, just set the // head pointer. The constructor for the // list entry has already set the next and // back pointers. // pHead = pNewEntry; } else { // // Insert this at the end of the circular // doubly-linked list. // pNewEntry->pBack = pHead->pBack; pNewEntry->pNext = pHead; pHead->pBack->pNext = pNewEntry; pHead->pBack = pNewEntry; } return; } VOID CCookieList::InsertSortedDisplay( ComponentData *pScopeControl, SchemaObject *pNewObject ) /*** Notes: This function inserts the object into the sorted display list. If the object is a class and the ComponentData interface pointer is provided, this routine will also create a cookie for this object and insert the scope item into the view. ***/ { HRESULT hr; CCookieListEntry *pNewEntry = NULL, *pCurrent = NULL; // NTRAID#NTBUG9-562405-2002/03/04-dantra-Possible use of uninitalized SCOPEDATAITEM SCOPEDATAITEM ScopeItem={0}; Cookie *pNewCookie= NULL; int compare; // // If this cookie list is empty, there's nothing // in the display and we don't need to do anything. // if ( !pHead ) { return; } // // Allocate a new cookie list entry. If we can't // do nothing. The display will be out of sync // until the user refreshes. // pNewEntry = new CCookieListEntry; if ( !pNewEntry ) { return; } // // Prepare the required mmc structures. // if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) { if ( !pScopeControl ) { // // If there's no scope control, we can't insert anything. // delete pNewEntry; return; } // prefix believes that this allocation (or construction) may throw // an exception, and if an exception is thrown, pNewEntry is // leaked. After a lot of digging, it's possible that a // CMemoryException instance may be thrown by one of the base // classes of one the members of CBaseCookieBlock, which is a base // clase of Cookie. // NTRAID#NTBUG9-294879-2001/01/26-sburns try { pNewCookie = new Cookie( SCHMMGMT_CLASS, pParentCookie->QueryNonNULLMachineName() ); } catch (...) { delete pNewEntry; return; } if ( !pNewCookie ) { // // If we can't allocate a cookie, do nothing. // delete pNewEntry; return; } pNewCookie->pParentCookie = pParentCookie; pNewCookie->strSchemaObject = pNewObject->commonName; pParentCookie->m_listScopeCookieBlocks.AddHead( (CBaseCookieBlock*)pNewCookie ); pNewEntry->pCookie = pNewCookie; ::ZeroMemory( &ScopeItem, sizeof(ScopeItem) ); ScopeItem.displayname = MMC_CALLBACK; ScopeItem.nState = 0; ScopeItem.lParam = reinterpret_cast((CCookie*)pNewCookie); ScopeItem.nImage = pScopeControl->QueryImage( *pNewCookie, FALSE ); ScopeItem.nOpenImage = pScopeControl->QueryImage( *pNewCookie, TRUE ); } // // Should this be the new head of the list? // compare = pNewObject->ldapDisplayName.CompareNoCase( pHead->pCookie->strSchemaObject ); if ( compare < 0 ) { if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) { // // Insert this into the scope pane. // ScopeItem.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_STATE | SDI_PARAM | SDI_NEXT | SDI_CHILDREN; ScopeItem.cChildren = 0; ScopeItem.relativeID = pHead->hScopeItem; hr = pScopeControl->m_pConsoleNameSpace->InsertItem( &ScopeItem ); pNewEntry->hScopeItem = ScopeItem.ID; pNewEntry->pCookie->m_hScopeItem = ScopeItem.ID; } else { hr = S_OK; } if ( SUCCEEDED(hr) ) { pNewEntry->pNext = pHead; pNewEntry->pBack = pHead->pBack; pHead->pBack->pNext = pNewEntry; pHead->pBack = pNewEntry; pHead = pNewEntry; } else { delete pNewEntry; delete pNewCookie; } return; } // // Determine the sorted insertion point. The sorted list is circular. // pCurrent = pHead; while ( pCurrent->pNext != pHead ) { compare = pNewObject->ldapDisplayName.CompareNoCase( pCurrent->pNext->pCookie->strSchemaObject ); if ( compare < 0 ) { break; } pCurrent = pCurrent->pNext; } // // We want to insert the new object after pCurrent. // if ( pNewObject->schemaObjectType == SCHMMGMT_CLASS ) { ScopeItem.mask = SDI_STR | SDI_IMAGE | SDI_OPENIMAGE | SDI_STATE | SDI_PARAM | SDI_PREVIOUS | SDI_CHILDREN; ScopeItem.cChildren = 0; ScopeItem.relativeID = pCurrent->hScopeItem; hr = pScopeControl->m_pConsoleNameSpace->InsertItem( &ScopeItem ); pNewEntry->hScopeItem = ScopeItem.ID; pNewEntry->pCookie->m_hScopeItem = ScopeItem.ID; } else { hr = S_OK; } if ( SUCCEEDED( hr )) { pNewEntry->pNext = pCurrent->pNext; pNewEntry->pBack = pCurrent; pCurrent->pNext->pBack = pNewEntry; pCurrent->pNext = pNewEntry; } else { delete pNewEntry; delete pNewCookie; } return; } bool CCookieList::DeleteCookie(Cookie* pCookie) { bool result = false; if (!pHead) { return result; } // walk the links and stop when the scope item matches. // Since the list is circular, // we use pHead as the sentinal value instead of null. CCookieListEntry* pCurrent = pHead; do { ASSERT(pCurrent); if (pCurrent->pCookie == pCookie) { // Remove the node from the list pCurrent->pBack->pNext = pCurrent->pNext; pCurrent->pNext->pBack = pCurrent->pBack; if (pCurrent == pHead) { pHead = pCurrent->pNext; } result = true; delete pCurrent; break; } pCurrent = pCurrent->pNext; } while (pCurrent != pHead); return result; } void CCookieList::DeleteAll() { if (!pHead) { return; } CCookieListEntry* pCurrent = pHead; do { CCookieListEntry* next = pCurrent->pNext; delete pCurrent; pCurrent = next; } while (pCurrent != pHead); pHead = 0; } CString GetHelpFilename() { TCHAR buf[MAX_PATH + 1]; UINT result = ::GetSystemWindowsDirectory(buf, MAX_PATH); ASSERT(result != 0 && result <= MAX_PATH); CString f(buf); f += TEXT("\\help\\schmmgmt.hlp"); return f; } BOOL ShowHelp( HWND hParent, WPARAM wParam, LPARAM lParam, const DWORD ids[], BOOL fContextMenuHelp ) { HWND hWndMain = NULL; UINT uCommand = 0; if( !fContextMenuHelp ) { // The user has clicked ? and the control, or just F1 (if enabled) const LPHELPINFO pHelpInfo = (LPHELPINFO)lParam; if (pHelpInfo && pHelpInfo->iContextType == HELPINFO_WINDOW) { hWndMain = (HWND) pHelpInfo->hItemHandle; uCommand = HELP_WM_HELP; } } else { hWndMain = (HWND) wParam; uCommand = HELP_CONTEXTMENU; // Optimization for non-static enabled windows. // This way users don't have to do an extra menu click // $$ don't know why this call returns NULL all the time // HWND hWnd = ChildWindowFromPoint( hParent, CPoint(lParam) ); // if( hWnd ) // hWndMain = hWnd; if( -1 != GET_X_LPARAM(lParam) && -1 != GET_Y_LPARAM(lParam) && hParent && hWndMain != hParent ) { uCommand = HELP_WM_HELP; } } if( hWndMain && uCommand ) { // Display context help for a control ::WinHelp( hWndMain, GetHelpFilename(), uCommand, (DWORD_PTR) ids ); } return TRUE; } #if 0 VOID DebugTrace( LPWSTR Format, ... ) { WCHAR DbgString[1024]; va_list arglist; int Length; // // Format the output into a buffer and then print it. // va_start(arglist, Format); Length = wvsprintf( DbgString, Format, arglist ); va_end(arglist); ASSERT( Length <= 1024 ); ASSERT( Length != 0 ); OutputDebugString( DbgString ); return; } #else VOID DebugTrace( LPWSTR, ... ) { ; } #endif // Attempt to locate a message in a given module. Return the message string // if found, the empty string if not. // // flags - FormatMessage flags to use // // module - module handle of message dll to look in, or 0 to use the system // message table. // // code - message code to look for CString getMessageHelper(DWORD flags, HMODULE module, HRESULT code) { ASSERT(code); ASSERT(flags & FORMAT_MESSAGE_ALLOCATE_BUFFER); CString message; TCHAR* sys_message = 0; DWORD result = ::FormatMessage( flags, module, static_cast(code), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&sys_message), 0, 0); if (result) { ASSERT(sys_message); if (sys_message) { message = sys_message; ::LocalFree(sys_message); } } return message; } #define MAX_ERROR_BUF 2048 HRESULT GetLastADsError( HRESULT hr, CString& refErrorMsg, CString& refName ) { ASSERT(FAILED(hr)); refErrorMsg.Empty(); refName.Empty(); if (!FAILED(hr)) { return hr; } if( FACILITY_WIN32 == HRESULT_FACILITY(hr) ) { DWORD dwLastError = 0; WCHAR szErrorBuf[ MAX_ERROR_BUF + 1 ]; WCHAR szNameBuf[ MAX_ERROR_BUF + 1 ]; //Get extended error value. HRESULT hr_return = ADsGetLastError( &dwLastError, szErrorBuf, MAX_ERROR_BUF, szNameBuf, MAX_ERROR_BUF ); ASSERT( SUCCEEDED(hr_return) ); if( SUCCEEDED(hr_return) && dwLastError ) { refErrorMsg = szErrorBuf; refName = szNameBuf; return HRESULT_FROM_WIN32( dwLastError ); } } return hr; } // Attempts to locate message strings for various facility codes in the // HRESULT. If fTryADSIExtError is true, check ADsGetLastError() first CString GetErrorMessage( HRESULT hr, BOOL fTryADSIExtError /* = FALSE */ ) { ASSERT(FAILED(hr)); if (!FAILED(hr)) { // no messages for success! return CString(); } CString strExtMsg; if( fTryADSIExtError && FACILITY_WIN32 == HRESULT_FACILITY(hr) ) { DWORD dwLastError = 0; WCHAR szErrorBuf[ MAX_ERROR_BUF + 1 ]; WCHAR szNameBuf[ MAX_ERROR_BUF + 1 ]; //Get extended error value. HRESULT hr_return = ADsGetLastError( &dwLastError, szErrorBuf, MAX_ERROR_BUF, szNameBuf, MAX_ERROR_BUF ); ASSERT( SUCCEEDED(hr_return) ); if( SUCCEEDED(hr_return) && dwLastError ) { hr = HRESULT_FROM_WIN32( dwLastError ); strExtMsg = szErrorBuf; } } int code = HRESULT_CODE(hr); CString message; // default is the system error message table HMODULE module = 0; DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; int facility = HRESULT_FACILITY(hr); switch (facility) { case FACILITY_WIN32: // 0x7 { // included here: // lanman error codes (in it's own dll) // dns // winsock static HMODULE lm_err_res_dll = 0; if (code >= NERR_BASE && code <= MAX_NERR) { // use the net error message resource dll if (lm_err_res_dll == 0) { lm_err_res_dll = ::LoadLibraryEx( L"netmsg.dll", 0, LOAD_LIBRARY_AS_DATAFILE); } module = lm_err_res_dll; flags |= FORMAT_MESSAGE_FROM_HMODULE; } break; } case 0x0: { if (code >= 0x5000 && code <= 0x50FF) { // It's an ADSI error. They put the facility code (5) in the // wrong place! static HMODULE adsi_err_res_dll = 0; // use the net error message resource dll if (adsi_err_res_dll == 0) { adsi_err_res_dll = ::LoadLibraryEx( L"activeds.dll", 0, LOAD_LIBRARY_AS_DATAFILE); } module = adsi_err_res_dll; flags |= FORMAT_MESSAGE_FROM_HMODULE; // the message dll expects the entire error code code = hr; } break; } default: { // do nothing break; } } message = getMessageHelper(flags, module, code); #ifdef SHOW_EXT_LDAP_MSG if( !strExtMsg.IsEmpty() ) message += L"\n" + strExtMsg; #endif //SHOW_EXT_LDAP_MSG if (message.IsEmpty()) { message.LoadString(IDS_UNKNOWN_ERROR_MESSAGE); } return message; } // // Get range from edit controls, Verify the range, attempt to correct, make sure lower <= upper. // // an exception will be thrown in case of an error. // void DDXV_VerifyAttribRange( CDataExchange *pDX, BOOL fIsSigned, UINT idcLower, CString & strLower, UINT idcUpper, CString & strUpper ) { INT64 llLower = 0; INT64 llUpper = 0; ASSERT( pDX ); ASSERT( pDX->m_pDlgWnd ); // Update the values. llLower = DDXV_SigUnsigINT32Value( pDX, fIsSigned, idcLower, strLower ); llUpper = DDXV_SigUnsigINT32Value( pDX, fIsSigned, idcUpper, strUpper ); #ifdef ENABLE_NEGATIVE_INT // verify that lower <= upper -- only if supporting ENABLE_NEGATIVE_INT if ( pDX->m_bSaveAndValidate && !strLower.IsEmpty() && !strUpper.IsEmpty() ) { if( llLower > llUpper ) { DoErrMsgBox( pDX->m_pDlgWnd->m_hWnd, TRUE, IDS_ERR_EDIT_MINMAX ); pDX->Fail(); // we are still at the second edit control. } } #endif //ENABLE_NEGATIVE_INT } // // Get string from an edit control, verify it attempting to correct // // an exception will be thrown in case of an error. // // Returns corrected value // INT64 DDXV_SigUnsigINT32Value( CDataExchange *pDX, BOOL fIsSigned, UINT idc, CString & str ) { INT64 llVal = 0; HRESULT hr = S_OK; ASSERT( pDX ); ASSERT( pDX->m_pDlgWnd ); // Get/Put the string DDX_Text( pDX, idc, str ); if ( pDX->m_bSaveAndValidate ) { if( !str.IsEmpty() ) { hr = GetSafeINT32FromString( pDX->m_pDlgWnd, llVal, str, fIsSigned, GETSAFEINT_ALLOW_CANCEL ); if( FAILED(hr) ) { pDX->Fail(); } else if( S_VALUE_MODIFIED == hr ) { // update the string in case of some conversion things ('010' --> '10') // or if the value was changed pDX->m_pDlgWnd->SetDlgItemText( idc, str ); } } } return llVal; } // // Converts a string to a DWORD, asks to correct to be within the range. // returns HRESULT: // S_OK llDst is the value from string // S_VALUE_MODIFIED llDst is the truncated value, strSrc is updated // E_ABORT llDst is unchanged; E_ABORT may be returned only if fAllowCancel is TRUE // HRESULT GetSafeSignedDWORDFromString( CWnd * pwndParent, DWORD & dwDst, CString & strSrc, BOOL fIsSigned, BOOL fAllowCancel /* =FALSE */) { INT64 llDst = 0; HRESULT hr = GetSafeINT32FromString( pwndParent, llDst, strSrc, fIsSigned, fAllowCancel ); if( SUCCEEDED( hr ) ) dwDst = (DWORD) llDst; return hr; } // // *** internal use *** // Converts a string to a INT64, asks to correct to be within the range. // returns HRESULT: // S_OK llDst is the value from string // S_VALUE_MODIFIED llDst is the truncated value, strSrc is updated // E_ABORT llDst is the truncated value // E_ABORT only happens if fAllowCancel is TRUE // HRESULT GetSafeINT32FromString( CWnd * pwndParent, INT64 & llDst, CString & strSrc, BOOL fIsSigned, BOOL fAllowCancel) { CThemeContextActivator activator; HRESULT hr = S_OK; UINT nMessageBoxType = 0; CString szMsg; CString szSugestedNumber; BOOL fIsValidNumber = TRUE; BOOL fIsValidString = TRUE; ASSERT( pwndParent ); // the string must be limited in length & not empty ASSERT( !strSrc.IsEmpty() ); ASSERT( strSrc.GetLength() <= cchMinMaxRange ); fIsValidString = IsValidNumberString( strSrc ); llDst = _wtoi64( (LPCWSTR) strSrc ); fIsValidNumber = IsValidNumber32( llDst, fIsSigned ); szSugestedNumber.Format( fIsSigned ? g_INT32_FORMAT : g_UINT32_FORMAT, (DWORD) llDst ); if( !fIsValidString || !fIsValidNumber ) { szMsg.FormatMessage( !fIsValidString ? IDS_ERR_NUM_IS_ILLIGAL : IDS_ERR_INT_OVERFLOW, (LPCWSTR) strSrc, (LPCWSTR) szSugestedNumber ); // make sure the user wants to do it nMessageBoxType = (fAllowCancel ? MB_OKCANCEL : MB_OK) | MB_ICONEXCLAMATION ; if( IDOK == pwndParent->MessageBox( szMsg, g_MsgBoxErr, nMessageBoxType ) ) { strSrc = szSugestedNumber; hr = S_VALUE_MODIFIED; } else { hr = E_ABORT; } } else if( strSrc != szSugestedNumber ) { // fixing number formating strSrc = szSugestedNumber; hr = S_VALUE_MODIFIED; } return hr; } // // Verify & correct Min/Max for a signed/unsigned INT64 value // BOOL IsValidNumber32( INT64 & llVal, BOOL fIsSigned ) { #ifdef ENABLE_NEGATIVE_INT const INT64 llMinVal = fIsSigned ? (INT64) _I32_MIN : (INT64) 0 ; const INT64 llMaxVal = fIsSigned ? (INT64) _I32_MAX : (INT64) _UI32_MAX ; #else // if there is no negative numbers support, always use unsigned numbers const INT64 llMinVal = (INT64) 0; const INT64 llMaxVal = (INT64) _UI32_MAX; #endif BOOL fIsValid = FALSE; // if larger than 32bit number (signed/unsigned), truncate... if( llVal < llMinVal ) { llVal = llMinVal; } else if( llVal > llMaxVal ) { llVal = llMaxVal; } else { fIsValid = TRUE; } return fIsValid; } // // Search number string for illigal characters. // BOOL IsValidNumberString( CString & str ) { int i = 0; #ifdef ENABLE_NEGATIVE_INT if( str.GetLength() > 0 && // allow negative sign in front of the number g_chNegativeSign == str[ i ] ) { i++; // skip first character } #endif //ENABLE_NEGATIVE_INT for( ; i < str.GetLength(); i++ ) { if( !IsCharNumeric( str[i] ) ) return FALSE; } return TRUE; } ///////////////////////////////////////////////////////////////////////////// // ParsedEdit BEGIN_MESSAGE_MAP(CParsedEdit, CEdit) //{{AFX_MSG_MAP(CParsedEdit) ON_WM_CHAR() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // Initialize subclassing BOOL CParsedEdit::SubclassEdit( UINT nID, CWnd* pParent, int cchMaxTextSize ) // 0 == unlimited { ASSERT( IsInitialized() ); ASSERT( nID ); ASSERT( pParent ); ASSERT( pParent->GetDlgItem(nID) ); ASSERT( cchMaxTextSize >= 0 ); ( static_cast( pParent->GetDlgItem(nID) ) ) -> LimitText( cchMaxTextSize ) ; if( EDIT_TYPE_GENERIC == GetEditType() ) { return TRUE; // no need to subclass - everything is allowed. } else { return SubclassDlgItem(nID, pParent); } } ///////////////////////////////////////////////////////////////////////////// // Input character filter void CParsedEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { ASSERT( IsInitialized() ); // initialized? BOOL fIsSpecialChar = ( nChar < 0x20 ); BOOL fAllowChar = FALSE; if( fIsSpecialChar ) { fAllowChar = TRUE; // always allow control chars } else { // is this a digit? BOOL fIsDigit = IsCharNumeric( (TCHAR)nChar ); switch( GetEditType() ) { default: ASSERT( FALSE ); break; case EDIT_TYPE_GENERIC: // everything is allowed fAllowChar = TRUE; break; case EDIT_TYPE_INT32: case EDIT_TYPE_UINT32: { #ifdef ENABLE_NEGATIVE_INT const BOOL fAllowNegativeNumbers = TRUE; #else const BOOL fAllowNegativeNumbers = FALSE; #endif DWORD dwSel = GetSel(); // is the caret in the begining of the box BOOL fLineFront = ! LOWORD( dwSel ); // is the first character selected? (thus, typing anything will overide it) BOOL fIsSelFirstChar = fLineFront && HIWORD( dwSel ); BOOL fIsNegSign = ( (TCHAR)nChar == g_chNegativeSign ); WCHAR szBuf[ 2 ] = {0}; // we only need the first character to check for '-' // if the first character is selected, no matter what we type it will be overwritten // an empty value is a positive value. // rellies on left to right execution. BOOL fIsAlreadyNeg = ( !fIsSelFirstChar && GetWindowText( szBuf, 2 ) && g_chNegativeSign == szBuf[0] ); ASSERT( !fIsDigit || !fIsNegSign ); // cannot be both! if ( ( fIsDigit && // allow numeric if ... ( !fAllowNegativeNumbers || // ignore error checking if false !fLineFront || // not first position (fLineFront && !fIsAlreadyNeg)) // first pos & no '-' sign ) || ( fIsNegSign && // allow '-' if fAllowNegativeNumbers && // negatives are allowed FIsSigned() && // signed numbers are allowed !fIsAlreadyNeg && // the number was positive fLineFront // entering as the first character ) ) { fAllowChar = TRUE; } } break; case EDIT_TYPE_OID: // do a light checking -- allow digits & periods { if( fIsDigit || g_chPeriod == (TCHAR)nChar ) { fAllowChar = TRUE; } } break; } } if( fAllowChar ) { CEdit::OnChar(nChar, nRepCnt, nFlags); // permitted } else { // not permitted MessageBeep((UINT)-1); // Standard beep } } ///////////////////////////////////////////////////////////////////////////// // // Search a list of PCTSTR for a strValue, returns TRUE if found // rgszList[] last element must be NULL // // puIndex - optional pointer, will be set to the position of the value if found. // BOOL IsInList( PCTSTR rgszList[], const CString & strValue, UINT * puIndex /* = NULL */ ) { UINT uIndex = 0; while( rgszList[ uIndex ] ) { if( !strValue.CompareNoCase( rgszList[uIndex] ) ) { if( puIndex ) *puIndex = uIndex; return TRUE; } else uIndex++; } return FALSE; } #define ADS_SYSTEMFLAG_SCHEMA_CONSTRUCTED 0x04 #define ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT 0x10 // // Determine if the object pointed to by pIADsObject is a constructed object. // HRESULT IsConstructedObject( IADs *pIADsObject, BOOL & fIsConstructed ) { LONG fSysAttribs = 0; HRESULT hr = GetSystemAttributes( pIADsObject, fSysAttribs ); if( SUCCEEDED(hr) ) fIsConstructed = ADS_SYSTEMFLAG_SCHEMA_CONSTRUCTED & fSysAttribs; return hr; } // // Determine if the object pointed to by pIADsObject is category 1 object. // HRESULT IsCategory1Object( IADs *pIADsObject, BOOL & fIsCategory1 ) { LONG fSysAttribs = 0; HRESULT hr = GetSystemAttributes( pIADsObject, fSysAttribs ); if( SUCCEEDED(hr) ) fIsCategory1 = ADS_SYSTEMFLAG_SCHEMA_BASE_OBJECT & fSysAttribs; return hr; } // // Read object's System Attribute // HRESULT GetSystemAttributes( IADs *pIADsObject, LONG &fSysAttribs ) { HRESULT hr = E_FAIL; VARIANT AdsResult; if( !pIADsObject ) ASSERT( FALSE ); else { VariantInit( &AdsResult ); // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = pIADsObject->Get( const_cast((LPCTSTR)g_systemFlags), &AdsResult ); if ( SUCCEEDED( hr ) ) { ASSERT(AdsResult.vt == VT_I4); fSysAttribs = V_I4(&AdsResult); } else if( E_ADS_PROPERTY_NOT_FOUND == hr ) { fSysAttribs = 0; hr = S_OK; } VariantClear( &AdsResult ); } return hr; } HRESULT DissableReadOnlyAttributes( CWnd * pwnd, IADs *pIADsObject, const CDialogControlsInfo * pCtrls, UINT cCtrls ) { ASSERT( pwnd ); ASSERT( pIADsObject ); ASSERT( pCtrls ); ASSERT( cCtrls ); HRESULT hr = S_OK; CStringList strlist; do { // extract the list of allowed attributes hr = GetStringListElement( pIADsObject, &g_allowedAttributesEffective, strlist ); BREAK_ON_FAILED_HRESULT(hr); for( UINT ind = 0; ind < cCtrls; ind++ ) { BOOL fFound = FALSE; // search for needed attributes for( POSITION pos = strlist.GetHeadPosition(); !fFound && pos != NULL; ) { CString * pstr = &strlist.GetNext( pos ); if( !pstr->CompareNoCase( pCtrls[ind].m_pszAttributeName ) ) { fFound = TRUE; } } if( !fFound ) { ASSERT( pwnd->GetDlgItem( pCtrls[ind].m_nID ) ); if( pCtrls[ind].m_fIsEditBox ) reinterpret_cast( pwnd->GetDlgItem(pCtrls[ind].m_nID) )->SetReadOnly(); else pwnd->GetDlgItem(pCtrls[ind].m_nID)->EnableWindow( FALSE ); } } } while( FALSE ); return hr; } HRESULT GetStringListElement( IADs *pIADsObject, LPWSTR *lppPathNames, CStringList &strlist ) { ASSERT( pIADsObject ); ASSERT( lppPathNames ); ASSERT( *lppPathNames ); HRESULT hr = S_OK; VARIANT varAttributes; VariantInit( &varAttributes ); strlist.RemoveAll(); do { // build an array of one element hr = ADsBuildVarArrayStr( lppPathNames, 1, &varAttributes ); ASSERT_BREAK_ON_FAILED_HRESULT(hr); hr = pIADsObject->GetInfoEx( varAttributes, 0 ); BREAK_ON_FAILED_HRESULT(hr); hr = VariantClear( &varAttributes ); ASSERT_BREAK_ON_FAILED_HRESULT(hr); // Get all allowed attributes // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = pIADsObject->GetEx( CComBSTR(*lppPathNames), &varAttributes ); BREAK_ON_FAILED_HRESULT(hr); // Convert result to a string list hr = VariantToStringList( varAttributes, strlist ); ASSERT( SUCCEEDED(hr) || E_ADS_PROPERTY_NOT_FOUND == hr ); BREAK_ON_FAILED_HRESULT(hr); } while( FALSE ); VariantClear( &varAttributes ); return hr; } // FUTURE-2002-03/94/2002-dantra-Needs comments // NTRAID#NTBUG9-567089-2002/03/06-dantra-OIDHasValidFormat should use the safe string functions. bool OIDHasValidFormat (PCWSTR pszOidValue, int& rErrorTypeStrID) { rErrorTypeStrID = 0; bool bFormatIsValid = false; // NOTE: Safe Use - query length including NULL terminator int nLen = WideCharToMultiByte( CP_ACP, // code page 0, // performance and mapping flags pszOidValue, // wide-character string (int) wcslen (pszOidValue), // number of chars in string 0, // buffer for new string 0, // size of buffer 0, // default for unmappable chars 0); // set when default char used if ( nLen > 0 ) { nLen++; // account for Null terminator PSTR pszAnsiBuf = new CHAR[nLen]; if ( pszAnsiBuf ) { // NOTE: Safe Use ZeroMemory (pszAnsiBuf, nLen*sizeof(CHAR)); // NOTE: Safe Use - nLen obtained from first call to WideCharToMultiByte nLen = WideCharToMultiByte( CP_ACP, // code page 0, // performance and mapping flags pszOidValue, // wide-character string (int) wcslen (pszOidValue), // number of chars in string pszAnsiBuf, // buffer for new string nLen, // size of buffer 0, // default for unmappable chars 0); // set when default char used if ( nLen ) { // According to PhilH: // The first number is limited to // 0,1 or 2. The second number is // limited to 0 - 39 when the first // number is 0 or 1. Otherwise, any // number. // Also, according to X.208, there // must be at least 2 numbers. bFormatIsValid = true; size_t cbAnsiBufLen = strlen (pszAnsiBuf); // check for only digits and "." size_t nIdx = strspn (pszAnsiBuf, "0123456789.\0"); if ( nIdx > 0 && nIdx < cbAnsiBufLen ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_CONTAINS_NON_DIGITS; } // check for consecutive "."s - string not valid if present if ( bFormatIsValid && strstr (pszAnsiBuf, "..") ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_CONTAINS_CONSECUTIVE_DOTS; } // must begin with "0." or "1." or "2." bool bFirstNumberIs0 = false; bool bFirstNumberIs1 = false; bool bFirstNumberIs2 = false; if ( bFormatIsValid ) { if ( !strncmp (pszAnsiBuf, "0.", 2) ) bFirstNumberIs0 = true; else if ( !strncmp (pszAnsiBuf, "1.", 2) ) bFirstNumberIs1 = true; else if ( !strncmp (pszAnsiBuf, "2.", 2) ) bFirstNumberIs2 = true; if ( !bFirstNumberIs0 && !bFirstNumberIs1 && !bFirstNumberIs2 ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_MUST_START_WITH_0_1_2; } } if ( bFormatIsValid && ( bFirstNumberIs0 || bFirstNumberIs1 ) ) { PSTR pszBuf = pszAnsiBuf; pszBuf += 2; // there must be a number after the dot if ( strlen (pszBuf) ) { // truncate the string at the next dot, if any PSTR pszDot = strstr (pszBuf, "."); if ( pszDot ) pszDot[0] = 0; // convert the string to a number and check for range 0-39 int nValue = atoi (pszBuf); if ( nValue < 0 || nValue > 39 ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_0_1_MUST_BE_0_TO_39; } } else { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_MUST_HAVE_TWO_NUMBERS; } } // ensure no trailing "." if ( bFormatIsValid ) { if ( '.' == pszAnsiBuf[cbAnsiBufLen - 1] ) { bFormatIsValid = false; rErrorTypeStrID = IDS_OID_CANNOT_END_WITH_DOT; } } if ( bFormatIsValid ) { bFormatIsValid = false; CRYPT_ATTRIBUTE cryptAttr; // FUTURE-2002-03/94/2002-dantra-Although this is a safe usage of ZeroMemory, suggest changing // the definition CRYPT_ATTRIBUTE cryptAttr = {0} and removing the ZeroMemory call. ::ZeroMemory (&cryptAttr, sizeof (CRYPT_ATTRIBUTE)); cryptAttr.cValue = 0; cryptAttr.pszObjId = pszAnsiBuf; cryptAttr.rgValue = 0; DWORD cbEncoded = 0; BOOL bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, &cryptAttr, NULL, &cbEncoded); if ( cbEncoded > 0 ) { BYTE* pBuffer = new BYTE[cbEncoded]; if ( pBuffer ) { bResult = CryptEncodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, &cryptAttr, pBuffer, &cbEncoded); if ( bResult ) { DWORD cbStructInfo = 0; bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, pBuffer, cbEncoded, 0, 0, &cbStructInfo); if ( cbStructInfo > 0 ) { BYTE* pStructBuf = new BYTE[cbStructInfo]; if ( pStructBuf ) { bResult = CryptDecodeObject (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, PKCS_ATTRIBUTE, pBuffer, cbEncoded, 0, pStructBuf, &cbStructInfo); if ( bResult ) { CRYPT_ATTRIBUTE* pCryptAttr = (CRYPT_ATTRIBUTE*) pStructBuf; if ( !strcmp (pszAnsiBuf, pCryptAttr->pszObjId) ) { bFormatIsValid = true; } } delete [] pStructBuf; } } } delete [] pBuffer; } } } } else { DebugTrace(L"WideCharToMultiByte (%s) failed: 0x%x\n", pszOidValue, GetLastError ()); } delete [] pszAnsiBuf; } } else { rErrorTypeStrID = IDS_OID_MUST_NOT_BE_EMPTY; DebugTrace(L"WideCharToMultiByte (%s) return: 0x%x\n", pszOidValue, GetLastError ()); } return bFormatIsValid; } HRESULT DeleteObject( const CString& path, Cookie* pcookie, PCWSTR pszClass ) /*** This deletes an attribute from the schema ***/ { HRESULT hr = S_OK; do { if ( !pcookie ) { hr = E_INVALIDARG; break; } CComPtr spIADsPathname; hr = ::CoCreateInstance( CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (void**)&spIADsPathname); if ( FAILED(hr) ) { break; } // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = spIADsPathname->Set( CComBSTR(path), ADS_SETTYPE_FULL ); if ( FAILED(hr) ) { break; } // Get the RDN so that we have it for deleting CComBSTR sbstrRDN; hr = spIADsPathname->Retrieve( ADS_FORMAT_LEAF, &sbstrRDN ); if ( FAILED(hr) ) { break; } // Get the path to the parent container hr = spIADsPathname->RemoveLeafElement(); if ( FAILED(hr) ) { break; } CComBSTR sbstrParentPath; hr = spIADsPathname->Retrieve( ADS_FORMAT_X500, &sbstrParentPath ); if ( FAILED(hr) ) { break; } // Now open the parent object CComPtr spContainer; hr = ::AdminToolsOpenObject( sbstrParentPath, NULL, NULL, ADS_SECURE_AUTHENTICATION, IID_IADsContainer, (void**)&spContainer); if ( FAILED(hr) ) { break; } // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = spContainer->Delete( CComBSTR(pszClass), sbstrRDN ); if ( FAILED(hr) ) { break; } } while (false); return hr; } ////////////////////////////////////////////////////////////////// // Theming support HPROPSHEETPAGE MyCreatePropertySheetPage(AFX_OLDPROPSHEETPAGE* psp) { PROPSHEETPAGE_V3 sp_v3 = {0}; // NTRAID#NTBUG9-567166-2002/03/06-dantra-Possible buffer overrun in MyCreatePropertySheetPage CopyMemory (&sp_v3, psp, psp->dwSize); sp_v3.dwSize = sizeof(sp_v3); return (::CreatePropertySheetPage(&sp_v3)); } // Menu Helper HRESULT _InsertMenuHelper( LPCONTEXTMENUCALLBACK piCallback, long lInsertionPointID, int index, BOOL fEnabled, /* = TRUE */ BOOL fChecked /* = FALSE */) { CONTEXTMENUITEM MenuItem; MenuItem.lInsertionPointID = lInsertionPointID; MenuItem.fFlags = (fEnabled ? 0 : MF_GRAYED) | (fChecked ? MF_CHECKED : 0); MenuItem.fSpecialFlags = 0; MenuItem.strName = const_cast( (LPCTSTR)g_MenuStrings[index] ); MenuItem.strStatusBarText = const_cast( (LPCTSTR)g_StatusStrings[index] ); MenuItem.lCommandID = index; return piCallback->AddItem( &MenuItem ); } HRESULT SchemaOpenObject ( PCWSTR pathName, REFIID riid, void** object ) { return ( AdminToolsOpenObject ( const_cast((LPCWSTR) pathName), NULL, NULL, ADS_SECURE_AUTHENTICATION, riid, object ) ); }