1020 lines
23 KiB
C++
1020 lines
23 KiB
C++
/****
|
|
|
|
Cache.cpp
|
|
|
|
The schema caching routines to improve browsing performance.
|
|
|
|
Locking Note:
|
|
|
|
This schema cache design allows for rudimentary multi-thread
|
|
protection via the lookup routines and the ReleaseRef routines.
|
|
To date, we have not needed this type of protection, so it is
|
|
not implemented. All locking rules should be obeyed, however,
|
|
in case this protection is later needed.
|
|
|
|
****/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "macros.h"
|
|
USE_HANDLE_MACROS("SCHMMGMT(cache.cpp)")
|
|
|
|
#include "resource.h"
|
|
#include "cache.h"
|
|
#include "schmutil.h"
|
|
#include "compdata.h"
|
|
|
|
//
|
|
// The Schema Object.
|
|
//
|
|
|
|
SchemaObject::SchemaObject() {
|
|
|
|
//
|
|
// Initialize the list heads to NULL.
|
|
//
|
|
|
|
pNext = NULL;
|
|
pSortedListFlink = NULL;
|
|
pSortedListBlink = NULL;
|
|
|
|
isDefunct = FALSE;
|
|
dwClassType = (DWORD) -1; // invalid number
|
|
|
|
systemMayContain = NULL;
|
|
mayContain = NULL;
|
|
|
|
systemMustContain = NULL;
|
|
mustContain = NULL;
|
|
|
|
systemAuxiliaryClass = NULL;
|
|
auxiliaryClass = NULL;
|
|
|
|
SyntaxOrdinal = UINT_MAX; // invalid number
|
|
}
|
|
|
|
SchemaObject::~SchemaObject() {
|
|
|
|
ListEntry *pEntry, *pNextEntry;
|
|
|
|
//
|
|
// Empty any non-zero lists.
|
|
//
|
|
|
|
pEntry = systemMayContain;
|
|
while ( pEntry ) {
|
|
pNextEntry = pEntry->pNext;
|
|
delete pEntry;
|
|
pEntry = pNextEntry;
|
|
}
|
|
|
|
pEntry = mayContain;
|
|
while ( pEntry ) {
|
|
pNextEntry = pEntry->pNext;
|
|
delete pEntry;
|
|
pEntry = pNextEntry;
|
|
}
|
|
pEntry = systemMustContain;
|
|
while ( pEntry ) {
|
|
pNextEntry = pEntry->pNext;
|
|
delete pEntry;
|
|
pEntry = pNextEntry;
|
|
}
|
|
pEntry = mustContain;
|
|
while ( pEntry ) {
|
|
pNextEntry = pEntry->pNext;
|
|
delete pEntry;
|
|
pEntry = pNextEntry;
|
|
}
|
|
pEntry = systemAuxiliaryClass;
|
|
while ( pEntry ) {
|
|
pNextEntry = pEntry->pNext;
|
|
delete pEntry;
|
|
pEntry = pNextEntry;
|
|
}
|
|
pEntry = auxiliaryClass;
|
|
while ( pEntry ) {
|
|
pNextEntry = pEntry->pNext;
|
|
delete pEntry;
|
|
pEntry = pNextEntry;
|
|
}
|
|
|
|
//
|
|
// Done (the CStrings will clean themselves up).
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// The Schema Object cache.
|
|
//
|
|
|
|
SchemaObjectCache::SchemaObjectCache() {
|
|
|
|
pScopeControl = NULL;
|
|
|
|
//
|
|
// Initialize the hash table.
|
|
//
|
|
|
|
buckets = HASH_TABLE_SIZE;
|
|
hash_table = (SchemaObject**) LocalAlloc( LMEM_ZEROINIT,
|
|
sizeof( SchemaObject* ) * buckets );
|
|
|
|
if (hash_table != NULL)
|
|
{
|
|
memset(
|
|
hash_table,
|
|
0,
|
|
sizeof( SchemaObject* ) * buckets );
|
|
}
|
|
pSortedClasses = NULL;
|
|
pSortedAttribs = NULL;
|
|
|
|
fInitialized = FALSE;
|
|
|
|
}
|
|
|
|
SchemaObjectCache::~SchemaObjectCache() {
|
|
|
|
//
|
|
// Clear the hash table.
|
|
//
|
|
|
|
FreeAll();
|
|
LocalFree( hash_table );
|
|
hash_table = NULL;
|
|
}
|
|
|
|
VOID
|
|
SchemaObjectCache::FreeAll() {
|
|
|
|
SchemaObject *current, *next;
|
|
|
|
DebugTrace( L"SchemaObjectCache::FreeAll()\n" );
|
|
|
|
for ( UINT i = 0 ; i < buckets ; i++ ) {
|
|
|
|
current = hash_table[i];
|
|
|
|
while ( current ) {
|
|
next = current->pNext;
|
|
delete current;
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
memset(
|
|
&(hash_table[0]),
|
|
0,
|
|
sizeof( SchemaObject* ) * buckets );
|
|
|
|
pSortedClasses = NULL;
|
|
pSortedAttribs = NULL;
|
|
|
|
fInitialized = FALSE;
|
|
|
|
return;
|
|
}
|
|
|
|
UINT
|
|
SchemaObjectCache::CalculateHashKey(
|
|
CString HashKey
|
|
) {
|
|
|
|
int len = HashKey.GetLength();
|
|
LPCTSTR current = (LPCTSTR)HashKey;
|
|
int hash = 0;
|
|
|
|
for ( int i = 0 ; i < len ; i++ ) {
|
|
hash += (i+1) * ( (TCHAR) CharLower((LPTSTR) current[i]) );
|
|
}
|
|
|
|
hash %= buckets;
|
|
|
|
DebugTrace( L"SchemaObjectCache::CalculateHashKey %ls (len %li) == %li\n",
|
|
const_cast<LPWSTR>((LPCTSTR)HashKey),
|
|
len,
|
|
hash );
|
|
|
|
return hash;
|
|
}
|
|
|
|
HRESULT
|
|
SchemaObjectCache::InsertSchemaObject(
|
|
SchemaObject* Object
|
|
) {
|
|
|
|
SchemaObject* chain;
|
|
int bucket = CalculateHashKey( Object->commonName );
|
|
|
|
chain = hash_table[bucket];
|
|
hash_table[bucket] = Object;
|
|
Object->pNext = chain;
|
|
|
|
DebugTrace( L"Insert: %ls, %ls, %ls, --> %li\n",
|
|
const_cast<LPWSTR>((LPCTSTR)Object->ldapDisplayName),
|
|
const_cast<LPWSTR>((LPCTSTR)Object->commonName),
|
|
const_cast<LPWSTR>((LPCTSTR)Object->description),
|
|
bucket );
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
SchemaObjectCache::InsertSortedSchemaObject(
|
|
SchemaObject* Object
|
|
) {
|
|
|
|
SchemaObject *pCurrent = NULL;
|
|
SchemaObject *pHead = NULL;
|
|
BOOLEAN ChangeHead = TRUE;
|
|
|
|
if ( Object->schemaObjectType == SCHMMGMT_CLASS ) {
|
|
pCurrent = pSortedClasses;
|
|
} else {
|
|
ASSERT( Object->schemaObjectType == SCHMMGMT_ATTRIBUTE );
|
|
pCurrent = pSortedAttribs;
|
|
}
|
|
|
|
//
|
|
// If we haven't built the sorted list yet, then we
|
|
// don't need to insert this element into it.
|
|
//
|
|
|
|
if ( !pCurrent ) {
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// The sorted list is circular.
|
|
//
|
|
|
|
while ( ( 0 < ( Object->commonName.CompareNoCase(
|
|
pCurrent->commonName ) ) ) &&
|
|
( pCurrent != pHead ) ) {
|
|
|
|
if ( ChangeHead ) {
|
|
pHead = pCurrent;
|
|
ChangeHead = FALSE;
|
|
}
|
|
|
|
pCurrent = pCurrent->pSortedListFlink;
|
|
|
|
}
|
|
|
|
pCurrent->pSortedListBlink->pSortedListFlink = Object;
|
|
Object->pSortedListBlink = pCurrent->pSortedListBlink;
|
|
Object->pSortedListFlink = pCurrent;
|
|
pCurrent->pSortedListBlink = Object;
|
|
|
|
if ( ChangeHead ) {
|
|
|
|
if ( Object->schemaObjectType == SCHMMGMT_CLASS ) {
|
|
pSortedClasses = Object;
|
|
} else {
|
|
pSortedAttribs = Object;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// This functions behavior has been modified to support schema delete.
|
|
// Previously this function would return the first match to the ldapDisplayName,
|
|
// Now it will return the first match to the ldapDisplayName that is not defunct
|
|
|
|
SchemaObject*
|
|
SchemaObjectCache::LookupSchemaObject(
|
|
CString ldapDisplayName,
|
|
SchmMgmtObjectType ObjectType
|
|
) {
|
|
if ( !fInitialized ) {
|
|
LoadCache();
|
|
}
|
|
|
|
SchemaObject * pHead = 0;
|
|
if ( ObjectType == SCHMMGMT_ATTRIBUTE)
|
|
{
|
|
pHead = pSortedAttribs;
|
|
}
|
|
else
|
|
{
|
|
pHead = pSortedClasses;
|
|
}
|
|
|
|
SchemaObject * pObject = pHead;
|
|
BOOL fFound = FALSE;
|
|
|
|
ASSERT( pObject );
|
|
|
|
do {
|
|
if( ObjectType == pObject->schemaObjectType &&
|
|
!pObject->isDefunct &&
|
|
!pObject->ldapDisplayName.CompareNoCase(ldapDisplayName) )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
pObject = pObject->pSortedListFlink;
|
|
|
|
} while ( pObject != pHead );
|
|
|
|
if (!fFound)
|
|
{
|
|
pObject = 0;
|
|
}
|
|
|
|
return pObject;
|
|
|
|
/*
|
|
int length = 0;
|
|
int bucket = CalculateHashKey( ldapDisplayName );
|
|
SchemaObject* chain = hash_table[bucket];
|
|
|
|
if ( !fInitialized ) {
|
|
LoadCache();
|
|
}
|
|
|
|
while ( chain ) {
|
|
|
|
if ( ( ObjectType == chain->schemaObjectType ) &&
|
|
!chain->isDefunct &&
|
|
!ldapDisplayName.CompareNoCase( chain->ldapDisplayName ) ) {
|
|
|
|
DebugTrace( L"SchemaObjectCache::LookupSchemaObject %ls, chain depth %li.\n",
|
|
const_cast<LPWSTR>((LPCTSTR)ldapDisplayName),
|
|
length );
|
|
|
|
return chain;
|
|
|
|
} else {
|
|
|
|
chain = chain->pNext;
|
|
length++;
|
|
}
|
|
}
|
|
|
|
DebugTrace( L"SchemaObjectCache::LookupSchemaObject %ls (NO HIT), chain depth %li.\n",
|
|
const_cast<LPWSTR>((LPCTSTR)ldapDisplayName),
|
|
length );
|
|
|
|
//
|
|
// LOCKING NOTE: The simple ref counting and locking is not
|
|
// currently implemented. See note at the head of the file.
|
|
//
|
|
return NULL;
|
|
*/
|
|
}
|
|
|
|
|
|
//
|
|
// sequential search of the entire cache for an object with the given CN
|
|
//
|
|
// objectType is given to slightly speed-up the process.
|
|
//
|
|
SchemaObject*
|
|
SchemaObjectCache::LookupSchemaObjectByCN( LPCTSTR pszCN,
|
|
SchmMgmtObjectType objectType )
|
|
{
|
|
if ( !fInitialized ) {
|
|
LoadCache();
|
|
}
|
|
|
|
SchemaObject * pHead = 0;
|
|
if ( objectType == SCHMMGMT_ATTRIBUTE)
|
|
{
|
|
pHead = pSortedAttribs;
|
|
}
|
|
else
|
|
{
|
|
pHead = pSortedClasses;
|
|
}
|
|
|
|
SchemaObject * pObject = pHead;
|
|
BOOL fFound = FALSE;
|
|
|
|
ASSERT( pObject );
|
|
|
|
do {
|
|
if( objectType == pObject->schemaObjectType &&
|
|
!pObject->commonName.CompareNoCase(pszCN) )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
pObject = pObject->pSortedListFlink;
|
|
|
|
} while ( pObject != pHead );
|
|
|
|
//
|
|
// LOCKING NOTE: The simple ref counting and locking is not
|
|
// currently implemented. See note at the head of the file.
|
|
//
|
|
|
|
return fFound ? pObject : NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
SchemaObjectCache::ReleaseRef(
|
|
SchemaObject*
|
|
) {
|
|
|
|
//
|
|
// E_NOTIMPL
|
|
// See the note at the head of the file.
|
|
//
|
|
|
|
}
|
|
|
|
HRESULT
|
|
SchemaObjectCache::LoadCache(
|
|
VOID
|
|
)
|
|
/***
|
|
|
|
This routine executes a couple of DS searches to read the
|
|
relevant items out of the schema along with some attributes
|
|
of those items.
|
|
|
|
This information is cached.
|
|
|
|
***/
|
|
{
|
|
if ( fInitialized ) {
|
|
return S_OK;
|
|
}
|
|
|
|
LPWSTR Attributes[] = {
|
|
g_DisplayName,
|
|
g_CN,
|
|
g_Description,
|
|
g_MayContain,
|
|
g_MustContain,
|
|
g_SystemMayContain,
|
|
g_SystemMustContain,
|
|
g_AuxiliaryClass,
|
|
g_SystemAuxiliaryClass,
|
|
g_SubclassOf,
|
|
g_ObjectClassCategory,
|
|
g_AttributeSyntax,
|
|
g_omSyntax,
|
|
g_omObjectClass,
|
|
g_isDefunct,
|
|
g_GlobalClassID, // must be last
|
|
};
|
|
const DWORD AttributeCount = sizeof(Attributes) / sizeof(Attributes[0]);
|
|
|
|
ADS_SEARCH_HANDLE hSearchHandle = NULL;
|
|
HRESULT hr = S_OK;
|
|
CComPtr<IADs> ipADs;
|
|
|
|
AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
|
|
|
|
//
|
|
// Put up a wait cursor since
|
|
// this could take a little while. The cursor will
|
|
// revert when the the CWaitCursor goes out of scope.
|
|
//
|
|
CWaitCursor wait;
|
|
|
|
|
|
//
|
|
// Get the schema container path.
|
|
//
|
|
|
|
if ( NULL == pScopeControl )
|
|
{
|
|
ASSERT(FALSE);
|
|
return E_FAIL;
|
|
}
|
|
|
|
hr = pScopeControl->CheckSchemaPermissions( &ipADs );
|
|
if ( !ipADs )
|
|
{
|
|
ASSERT( FAILED(hr) );
|
|
|
|
if( E_FAIL == hr )
|
|
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_NO_SCHEMA_PATH );
|
|
else
|
|
DoErrMsgBox( ::GetActiveWindow(), TRUE, GetErrorMessage(hr,TRUE) );
|
|
|
|
return hr;
|
|
}
|
|
else if( FAILED(hr) )
|
|
hr = S_OK; // ignore the error. In case of an error, minimal permissions are assumed
|
|
|
|
|
|
//
|
|
// Open the schema container.
|
|
//
|
|
|
|
IDirectorySearch *pDSSearch = 0;
|
|
hr = ipADs->QueryInterface( IID_IDirectorySearch,
|
|
(void **)&pDSSearch );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
ASSERT(FALSE);
|
|
DoErrMsgBox( ::GetActiveWindow(), TRUE, IDS_ERR_NO_SCHEMA_PATH );
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set up the search preferences
|
|
//
|
|
|
|
static const int SEARCH_PREF_COUNT = 3;
|
|
ADS_SEARCHPREF_INFO prefs[SEARCH_PREF_COUNT];
|
|
|
|
// server side sort preferences
|
|
ADS_SORTKEY SortKey;
|
|
SortKey.pszAttrType = g_DisplayName;
|
|
SortKey.pszReserved = NULL;
|
|
SortKey.fReverseorder = 0;
|
|
|
|
prefs[0].dwSearchPref = ADS_SEARCHPREF_SORT_ON;
|
|
prefs[0].vValue.dwType = ADSTYPE_PROV_SPECIFIC;
|
|
prefs[0].vValue.ProviderSpecific.dwLength = sizeof(ADS_SORTKEY);
|
|
prefs[0].vValue.ProviderSpecific.lpValue = (LPBYTE) &SortKey;
|
|
|
|
// result page size
|
|
prefs[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
|
|
prefs[1].vValue.dwType = ADSTYPE_INTEGER;
|
|
prefs[1].vValue.Integer = 300; // get a bunch in one hit
|
|
|
|
// scope
|
|
prefs[2].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
|
|
prefs[2].vValue.dwType = ADSTYPE_INTEGER;
|
|
prefs[2].vValue.Integer = ADS_SCOPE_ONELEVEL; // one level
|
|
|
|
//
|
|
// Build the class search request.
|
|
//
|
|
|
|
hr = pDSSearch->SetSearchPreference(prefs, SEARCH_PREF_COUNT);
|
|
ASSERT( SUCCEEDED( hr ) );
|
|
|
|
hr =
|
|
pDSSearch->ExecuteSearch(
|
|
L"(objectCategory=classSchema)",
|
|
Attributes,
|
|
AttributeCount,
|
|
&hSearchHandle );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
ASSERT(FALSE);
|
|
pDSSearch->Release();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Cache these entries. We ignore error codes and try to
|
|
// process the attributes regardless.
|
|
//
|
|
|
|
hr = ProcessSearchResults( pDSSearch, hSearchHandle, SCHMMGMT_CLASS);
|
|
|
|
pDSSearch->CloseSearchHandle( hSearchHandle );
|
|
|
|
hr = pDSSearch->SetSearchPreference(prefs, SEARCH_PREF_COUNT);
|
|
ASSERT( SUCCEEDED( hr ) );
|
|
|
|
//
|
|
// This array index must match the array declared above!
|
|
//
|
|
|
|
Attributes[AttributeCount - 1] = g_GlobalAttributeID;
|
|
|
|
hr =
|
|
pDSSearch->ExecuteSearch(
|
|
L"(objectCategory=attributeSchema)",
|
|
Attributes,
|
|
AttributeCount,
|
|
&hSearchHandle );
|
|
|
|
if ( FAILED(hr) )
|
|
{
|
|
ASSERT(FALSE);
|
|
pDSSearch->Release();
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Cache these entries. Again, ignore the error code.
|
|
//
|
|
|
|
hr = ProcessSearchResults( pDSSearch, hSearchHandle, SCHMMGMT_ATTRIBUTE );
|
|
|
|
pDSSearch->CloseSearchHandle( hSearchHandle );
|
|
|
|
//
|
|
// Release the schema container.
|
|
//
|
|
|
|
pDSSearch->Release();
|
|
|
|
//
|
|
// Mark the cache as open for business.
|
|
//
|
|
|
|
fInitialized = TRUE;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
SchemaObjectCache::ProcessSearchResults(
|
|
IDirectorySearch *pDSSearch,
|
|
ADS_SEARCH_HANDLE hSearchHandle,
|
|
SchmMgmtObjectType ObjectType
|
|
) {
|
|
|
|
HRESULT hr = S_OK;
|
|
SchemaObject *schemaObject;
|
|
ADS_SEARCH_COLUMN Column;
|
|
|
|
while ( TRUE ) {
|
|
|
|
//
|
|
// Get the next row set. If there are no more rows, break.
|
|
// If there was some other error, try to skip over the
|
|
// troubled row.
|
|
//
|
|
|
|
hr = pDSSearch->GetNextRow( hSearchHandle );
|
|
|
|
if ( hr == S_ADS_NOMORE_ROWS ) {
|
|
break;
|
|
}
|
|
|
|
if ( hr != S_OK ) {
|
|
ASSERT( FALSE );
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Allocate a new schema object. If one could not be
|
|
// allocated, stop loading the schema since we're in a
|
|
// low memory condition.
|
|
//
|
|
|
|
schemaObject = new SchemaObject;
|
|
if ( !schemaObject ) {
|
|
AfxMessageBox(IDS_SCHEMA_NOT_FULLY_LOADED, MB_OK);
|
|
ASSERT( FALSE );
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Set the object type.
|
|
//
|
|
|
|
schemaObject->schemaObjectType = ObjectType;
|
|
|
|
//
|
|
// Get the common name column.
|
|
//
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_CN, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->commonName = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
//
|
|
// Get the ldap display name.
|
|
//
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_DisplayName, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->ldapDisplayName = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
//
|
|
// Get the description.
|
|
//
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_Description, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->description = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
//
|
|
// Is this object current active?
|
|
//
|
|
|
|
schemaObject->isDefunct = FALSE;
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_isDefunct, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
|
|
if ( (Column.pADsValues)->Boolean ) {
|
|
schemaObject->isDefunct = TRUE;
|
|
}
|
|
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
//
|
|
// Get the class specific data.
|
|
//
|
|
|
|
if ( ObjectType == SCHMMGMT_CLASS ) {
|
|
|
|
//
|
|
// Get the attributes and auxiliary classes for this class.
|
|
//
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_SystemMustContain, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->systemMustContain = MakeColumnList( &Column );
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_SystemMayContain, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->systemMayContain = MakeColumnList( &Column );
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_MustContain, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->mustContain = MakeColumnList( &Column );
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_MayContain, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->mayContain = MakeColumnList( &Column );
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_SystemAuxiliaryClass, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->systemAuxiliaryClass = MakeColumnList( &Column );
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_AuxiliaryClass, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->auxiliaryClass = MakeColumnList( &Column );
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_ObjectClassCategory, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->dwClassType = (Column.pADsValues)->Integer;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_SubclassOf, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->subClassOf = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
//
|
|
// Get the oid.
|
|
//
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_GlobalClassID, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->oid = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Get the attribute specific data.
|
|
//
|
|
|
|
if ( ObjectType == SCHMMGMT_ATTRIBUTE ) {
|
|
|
|
//
|
|
// Select a syntax string for the attribute.
|
|
//
|
|
|
|
CString strAttributeSyntax;
|
|
ADS_OCTET_STRING OmObjectClass;
|
|
UINT omSyntax = 0;
|
|
|
|
schemaObject->SyntaxOrdinal = SCHEMA_SYNTAX_UNKNOWN;
|
|
OmObjectClass.dwLength = 0;
|
|
OmObjectClass.lpValue = NULL;
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_AttributeSyntax, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
|
|
strAttributeSyntax = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_omSyntax, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
omSyntax = (Column.pADsValues)->Integer;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_omObjectClass, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
OmObjectClass = (Column.pADsValues)->OctetString;
|
|
}
|
|
|
|
schemaObject->SyntaxOrdinal = GetSyntaxOrdinal(
|
|
strAttributeSyntax, omSyntax, &OmObjectClass );
|
|
|
|
// OmObjectClass has a pointer which becomes invalid after FreeColumn()
|
|
if ( SUCCEEDED(hr) ) {
|
|
pDSSearch->FreeColumn( &Column );
|
|
OmObjectClass.dwLength = 0;
|
|
OmObjectClass.lpValue = NULL;
|
|
}
|
|
}
|
|
else
|
|
ASSERT( FALSE );
|
|
|
|
//
|
|
// Get the oid.
|
|
//
|
|
|
|
hr = pDSSearch->GetColumn( hSearchHandle, g_GlobalAttributeID, &Column );
|
|
|
|
if ( SUCCEEDED(hr) ) {
|
|
schemaObject->oid = (Column.pADsValues)->CaseIgnoreString;
|
|
pDSSearch->FreeColumn( &Column );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Insert this into the sorted item list.
|
|
//
|
|
|
|
InsertSortedTail( schemaObject );
|
|
|
|
//
|
|
// Insert this schema object into the cache.
|
|
//
|
|
|
|
InsertSchemaObject( schemaObject );
|
|
schemaObject = NULL;
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
VOID
|
|
SchemaObjectCache::InsertSortedTail(
|
|
SchemaObject* pObject
|
|
) {
|
|
|
|
SchemaObject **sorted_list;
|
|
SchemaObject *pHead;
|
|
|
|
//
|
|
// Find the correct list.
|
|
//
|
|
|
|
if ( pObject->schemaObjectType == SCHMMGMT_CLASS ) {
|
|
sorted_list = &pSortedClasses;
|
|
} else {
|
|
sorted_list = &pSortedAttribs;
|
|
}
|
|
|
|
//
|
|
// Actually insert the element.
|
|
//
|
|
|
|
if ( *sorted_list == NULL ) {
|
|
|
|
//
|
|
// This is the first element.
|
|
//
|
|
|
|
*sorted_list = pObject;
|
|
pObject->pSortedListFlink = pObject;
|
|
pObject->pSortedListBlink = pObject;
|
|
|
|
} else {
|
|
|
|
//
|
|
// This is not the first element;
|
|
//
|
|
|
|
pHead = *sorted_list;
|
|
|
|
pObject->pSortedListBlink = pHead->pSortedListBlink;
|
|
pHead->pSortedListBlink->pSortedListFlink = pObject;
|
|
pHead->pSortedListBlink = pObject;
|
|
pObject->pSortedListFlink = pHead;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ListEntry*
|
|
SchemaObjectCache::MakeColumnList(
|
|
PADS_SEARCH_COLUMN pColumn
|
|
) {
|
|
|
|
ListEntry *pHead = NULL, *pLast = NULL, *pCurrent = NULL;
|
|
|
|
for ( DWORD i = 0 ; i < pColumn->dwNumValues ; i++ ) {
|
|
|
|
pCurrent = new ListEntry;
|
|
|
|
//
|
|
// If we run out of memory, return what we made so far.
|
|
//
|
|
|
|
if ( !pCurrent ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If there's no head, remember this as the first.
|
|
// Otherwise, stick this on the end of the list
|
|
// and update the last pointer.
|
|
//
|
|
|
|
if ( !pHead ) {
|
|
pHead = pCurrent;
|
|
pLast = pCurrent;
|
|
} else {
|
|
pLast->pNext = pCurrent;
|
|
pLast = pCurrent;
|
|
}
|
|
|
|
//
|
|
// Record the value.
|
|
//
|
|
|
|
pCurrent->Attribute = pColumn->pADsValues[i].CaseIgnoreString;
|
|
DebugTrace( L"MakeColumnList recorded %ls.\n",
|
|
pColumn->pADsValues[i].CaseIgnoreString );
|
|
|
|
//
|
|
// That's it.
|
|
//
|
|
}
|
|
|
|
return pHead;
|
|
}
|
|
|
|
VOID
|
|
SchemaObjectCache::FreeColumnList(
|
|
ListEntry *pListHead
|
|
) {
|
|
|
|
//
|
|
// Delete the linked list.
|
|
//
|
|
|
|
ListEntry *pNext, *pCurrent;
|
|
|
|
if ( !pListHead ) {
|
|
return;
|
|
}
|
|
|
|
pCurrent = pListHead;
|
|
|
|
do {
|
|
|
|
pNext = pCurrent->pNext;
|
|
delete pCurrent;
|
|
|
|
pCurrent = pNext;
|
|
|
|
} while ( pCurrent );
|
|
|
|
return;
|
|
}
|