mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7237 lines
187 KiB
7237 lines
187 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996
|
|
//
|
|
// File: ldapsch.cxx
|
|
//
|
|
// Contents: LDAP Schema Parser
|
|
//
|
|
// History:
|
|
//----------------------------------------------------------------------------
|
|
#include "ldapc.hxx"
|
|
#pragma hdrstop
|
|
|
|
#define ADSI_LDAP_KEY TEXT("SOFTWARE\\Microsoft\\ADs\\Providers\\LDAP")
|
|
#define SCHEMA_DIR_NAME TEXT("SchCache\\")
|
|
#define SCHEMA_FILE_NAME_EXT TEXT(".sch")
|
|
#define DEFAULT_SCHEMA_FILE_NAME TEXT("Default")
|
|
#define DEFAULT_SCHEMA_FILE_NAME_WITH_EXT TEXT("Default.sch")
|
|
#define SCHEMA_FILE_NAME TEXT("File")
|
|
#define SCHEMA_TIME TEXT("Time")
|
|
#define SCHEMA_PROCESSAUX TEXT("ProcessAUX")
|
|
|
|
|
|
#define MAX_LOOP_COUNT 30 // Maximum depth of schema class tree
|
|
#define ENTER_SCHEMA_CRITSECT() EnterCriticalSection(&g_SchemaCritSect)
|
|
#define LEAVE_SCHEMA_CRITSECT() LeaveCriticalSection(&g_SchemaCritSect)
|
|
|
|
#define ENTER_SUBSCHEMA_CRITSECT() EnterCriticalSection(&g_SubSchemaCritSect)
|
|
#define LEAVE_SUBSCHEMA_CRITSECT() LeaveCriticalSection(&g_SubSchemaCritSect)
|
|
|
|
#define ENTER_DEFAULTSCHEMA_CRITSECT() EnterCriticalSection(&g_DefaultSchemaCritSect)
|
|
#define LEAVE_DEFAULTSCHEMA_CRITSECT() LeaveCriticalSection(&g_DefaultSchemaCritSect)
|
|
|
|
|
|
|
|
#define ID_ATTRTYPES 1
|
|
#define ID_OBJCLASSES 2
|
|
#define ID_DITCONTENTRULES 3
|
|
|
|
#ifdef WIN95
|
|
int ConvertToAscii( WCHAR *pszUnicode, char **pszAscii );
|
|
#endif
|
|
|
|
//
|
|
// Constants used to determine what elements of string array to free.
|
|
//
|
|
const int FREE_ALL = 0;
|
|
const int FREE_ARRAY_NOT_ELEMENTS = 1;
|
|
const int FREE_ALL_BUT_FIRST = 2;
|
|
|
|
|
|
|
|
//
|
|
// RFC 2252
|
|
//
|
|
KWDLIST g_aSchemaKeywordList[] =
|
|
{
|
|
{ TOKEN_NAME, TEXT("NAME") },
|
|
{ TOKEN_DESC, TEXT("DESC") },
|
|
{ TOKEN_OBSOLETE, TEXT("OBSOLETE") },
|
|
{ TOKEN_SUP, TEXT("SUP") },
|
|
{ TOKEN_EQUALITY, TEXT("EQUALITY") },
|
|
{ TOKEN_ORDERING, TEXT("ORDERING") },
|
|
{ TOKEN_SUBSTR, TEXT("SUBSTR") },
|
|
{ TOKEN_SYNTAX, TEXT("SYNTAX") },
|
|
{ TOKEN_SINGLE_VALUE, TEXT("SINGLE-VALUE") },
|
|
{ TOKEN_COLLECTIVE, TEXT("COLLECTIVE") },
|
|
{ TOKEN_DYNAMIC, TEXT("DYNAMIC") },
|
|
{ TOKEN_NO_USER_MODIFICATION, TEXT("NO-USER-MODIFICATION") },
|
|
{ TOKEN_USAGE, TEXT("USAGE") },
|
|
{ TOKEN_ABSTRACT, TEXT("ABSTRACT") },
|
|
{ TOKEN_STRUCTURAL, TEXT("STRUCTURAL") },
|
|
{ TOKEN_AUXILIARY, TEXT("AUXILIARY") },
|
|
{ TOKEN_MUST, TEXT("MUST") },
|
|
{ TOKEN_MAY, TEXT("MAY") },
|
|
{ TOKEN_AUX, TEXT("AUX") },
|
|
{ TOKEN_NOT, TEXT("NOT") }
|
|
// FORM
|
|
};
|
|
|
|
DWORD g_dwSchemaKeywordListSize = sizeof(g_aSchemaKeywordList)/sizeof(KWDLIST);
|
|
|
|
CRITICAL_SECTION g_SchemaCritSect;
|
|
CRITICAL_SECTION g_DefaultSchemaCritSect;
|
|
CRITICAL_SECTION g_SubSchemaCritSect;
|
|
SCHEMAINFO *g_pSchemaInfoList = NULL; // Link list of cached schema info
|
|
SCHEMAINFO *g_pDefaultSchemaInfo = NULL;
|
|
|
|
//
|
|
// Non-AD sd control.
|
|
//
|
|
#define ADSI_LDAP_OID_SECDESC_OLD L"1.2.840.113556.1.4.416"
|
|
|
|
typedef struct _subschemalist {
|
|
LPWSTR pszLDAPServer;
|
|
LPWSTR pszSubSchemaEntry;
|
|
BOOL fPagingSupported;
|
|
BOOL fSortingSupported;
|
|
BOOL fDomScopeSupported;
|
|
BOOL fTalkingToAD;
|
|
BOOL fTalkingToEnhancedAD;
|
|
BOOL fVLVSupported;
|
|
BOOL fAttribScopedSupported;
|
|
struct _subschemalist *pNext;
|
|
BOOL fNoDataGot;
|
|
DWORD dwSecDescType;
|
|
} SCHEMALIST, *PSCHEMALIST;
|
|
|
|
//
|
|
// The fNoDataReturned will be set for v2 servers that do not
|
|
// have a subSchemaSubEntry, this will prevent hitting the server
|
|
// multiple times for the same data.
|
|
//
|
|
|
|
typedef SCHEMALIST ROOTDSENODE, *PROOTDSENODE;
|
|
|
|
PSCHEMALIST gpSubSchemaList = NULL;
|
|
|
|
static DWORD dwSubSchemaSubEntryCount = 0;
|
|
|
|
HRESULT
|
|
GetSchemaInfoTime(
|
|
LPTSTR pszServer,
|
|
LPTSTR pszSubSchemaSubEntry,
|
|
LPTSTR *ppszTimeReg,
|
|
LPTSTR *ppszTimeDS,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
);
|
|
|
|
HRESULT
|
|
LdapReadSchemaInfoFromServer(
|
|
LPTSTR pszLDAPPath,
|
|
LPTSTR pszSubSchemaSubEntry,
|
|
LPTSTR pszTimeReg,
|
|
LPTSTR pszTimeDS,
|
|
SCHEMAINFO **ppSchemaInfo,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
);
|
|
|
|
HRESULT
|
|
ReadRootDSENode(
|
|
LPWSTR pszLDAPServer,
|
|
PROOTDSENODE pRootDSENode,
|
|
OUT BOOL * pfBoundOk, // optional, can be NULL
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
);
|
|
|
|
HRESULT
|
|
LdapReadDefaultSchema(
|
|
LPTSTR pszServer,
|
|
CCredentials &Credentials,
|
|
SCHEMAINFO **ppSchemaInfo
|
|
);
|
|
|
|
HRESULT FillPropertyInfoArray(
|
|
LPTSTR *aAttrTypes,
|
|
DWORD dwCount,
|
|
PROPERTYINFO **paProperties,
|
|
DWORD *pnProperties,
|
|
SEARCHENTRY **paSearchTable
|
|
);
|
|
|
|
HRESULT FillClassInfoArray(
|
|
LPTSTR *aObjectClasses,
|
|
DWORD dwCount,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount,
|
|
CLASSINFO **paClasses,
|
|
DWORD *pnClasses,
|
|
SEARCHENTRY **paSearchTable
|
|
);
|
|
|
|
HRESULT FillAuxClassInfoArray(
|
|
LPTSTR *aDITContentRules,
|
|
DWORD dwCount,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount,
|
|
CLASSINFO *aClasses,
|
|
DWORD nClasses,
|
|
SEARCHENTRY *aSearchTable
|
|
);
|
|
|
|
HRESULT ProcessClassInfoArray(
|
|
CLASSINFO *aClasses,
|
|
DWORD nClasses,
|
|
SEARCHENTRY *paSearchTable,
|
|
BOOL fProcessAUX = FALSE
|
|
);
|
|
|
|
HRESULT ProcessPropertyInfoArray(
|
|
PROPERTYINFO *aProperties,
|
|
DWORD nProperties,
|
|
SEARCHENTRY **paSearchTable
|
|
);
|
|
|
|
DWORD ReadSchemaInfoFromRegistry(
|
|
HKEY hKey,
|
|
LPWSTR pszServer,
|
|
LPTSTR **paValuesAttribTypes,
|
|
int *pnCountAttribTypes,
|
|
LPTSTR **paValuesObjClasses,
|
|
int *pnCountObjClasses,
|
|
LPTSTR **paValuesRules,
|
|
int *pnCountRules,
|
|
LPBYTE *pBuffer
|
|
);
|
|
|
|
DWORD StoreSchemaInfoInRegistry(
|
|
HKEY hKey,
|
|
LPTSTR pszServer,
|
|
LPTSTR pszTime,
|
|
LPTSTR *aValuesAttribTypes,
|
|
int nCountAttribTypes,
|
|
LPTSTR *aValuesObjClasses,
|
|
int nCountObjClasses,
|
|
LPTSTR *aValuesRules,
|
|
int nCountRules,
|
|
BOOL fProcessAUX
|
|
);
|
|
|
|
HRESULT
|
|
AttributeTypeDescription(
|
|
LPTSTR pszAttrType,
|
|
PPROPERTYINFO pPropertyInfo,
|
|
LPWSTR **pppszNames,
|
|
PDWORD pdwNameCount
|
|
);
|
|
|
|
HRESULT
|
|
ObjectClassDescription(
|
|
LPTSTR pszDescription,
|
|
PCLASSINFO pClassInfo,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount,
|
|
LPWSTR **pppszNames,
|
|
PDWORD pdwNameCount
|
|
);
|
|
|
|
HRESULT DITContentRuleDescription(
|
|
LPTSTR pszObjectClass,
|
|
PCLASSINFO pClassInfo,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount
|
|
);
|
|
|
|
//
|
|
// Helper routine that adds new elements to the property info array.
|
|
//
|
|
HRESULT AddNewNamesToPropertyArray(
|
|
PROPERTYINFO **ppPropArray,
|
|
DWORD dwCurPos,
|
|
DWORD dwCount,
|
|
LPWSTR *ppszNewNames,
|
|
DWORD dwNewNameCount
|
|
);
|
|
|
|
//
|
|
// Helper routine that adds new elements to the class info array.
|
|
//
|
|
HRESULT AddNewNamesToClassArray(
|
|
CLASSINFO **ppClassArray,
|
|
DWORD dwCurPos,
|
|
DWORD dwCount,
|
|
LPWSTR *ppszNewNames,
|
|
DWORD dwNewNameCount
|
|
);
|
|
|
|
//
|
|
// The 3rd param was added to work around bad schema data.
|
|
//
|
|
HRESULT Oid(
|
|
CSchemaLexer * pTokenizer,
|
|
LPTSTR *ppszOID,
|
|
BOOL fNoGuid = FALSE
|
|
);
|
|
|
|
HRESULT Oids(
|
|
CSchemaLexer * pTokenizer,
|
|
LPTSTR **pOIDs,
|
|
DWORD *pnNumOfOIDs
|
|
);
|
|
|
|
HRESULT PropOids(
|
|
CSchemaLexer * pTokenizer,
|
|
int **pOIDs,
|
|
DWORD *pnNumOfOIDs,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount
|
|
);
|
|
|
|
HRESULT DirectoryString(
|
|
CSchemaLexer * pTokenizer,
|
|
LPTSTR *ppszDirString
|
|
);
|
|
|
|
//
|
|
// Returns *pdwCount strings in ppszDirStrings.
|
|
//
|
|
HRESULT DirectoryStrings(
|
|
CSchemaLexer * pTokenizer,
|
|
LPTSTR **pppszDirStrings,
|
|
PDWORD pdwCount
|
|
);
|
|
|
|
void FreeDirectoryStrings(
|
|
LPTSTR *ppszDirStrings,
|
|
DWORD dwCount,
|
|
DWORD dwElementsToFree= FREE_ALL
|
|
);
|
|
|
|
VOID SortAndRemoveDuplicateOIDs(
|
|
int *pOIDs,
|
|
DWORD *pnNumOfOIDs
|
|
);
|
|
|
|
int _cdecl searchentrycmp(
|
|
const void *s1,
|
|
const void *s2
|
|
);
|
|
|
|
long CompareUTCTime(
|
|
LPTSTR pszTime1,
|
|
LPTSTR pszTime2
|
|
);
|
|
|
|
BOOL
|
|
EquivalentServers(
|
|
LPWSTR pszTargetServer,
|
|
LPWSTR pszSourceServer
|
|
);
|
|
|
|
BOOL
|
|
EquivalentUsers(
|
|
LPWSTR pszTargetServer,
|
|
LPWSTR pszSourceServer
|
|
);
|
|
|
|
DWORD
|
|
GetDefaultServer(
|
|
DWORD dwPort,
|
|
BOOL fVerify,
|
|
LPWSTR szDomainDnsName,
|
|
LPWSTR szServerName,
|
|
BOOL fWriteable
|
|
);
|
|
|
|
//
|
|
// Makes a copy of a string array that has NULL as the last element.
|
|
// If the copy failed because of lack of memory NULL is returned.
|
|
//
|
|
LPTSTR *
|
|
CopyStringArray(
|
|
LPTSTR * ppszStr
|
|
)
|
|
{
|
|
LPTSTR * ppszRetVal = NULL;
|
|
DWORD dwCount = 0;
|
|
|
|
if (!ppszStr) {
|
|
BAIL_ON_FAILURE(E_FAIL);
|
|
}
|
|
|
|
//
|
|
// Get the count first.
|
|
//
|
|
while (ppszStr && ppszStr[dwCount]) {
|
|
dwCount++;
|
|
}
|
|
|
|
//
|
|
// Alloc memory for the array, + 1, is for the NULL string that
|
|
// acts as the delimiter for the array.
|
|
//
|
|
ppszRetVal = (LPTSTR *) AllocADsMem((dwCount+1) * sizeof(LPTSTR));
|
|
|
|
if (!ppszRetVal) {
|
|
BAIL_ON_FAILURE(E_OUTOFMEMORY);
|
|
}
|
|
|
|
for (DWORD dwCtr = 0; dwCtr <= dwCount; dwCtr++) {
|
|
if (ppszStr[dwCtr]) {
|
|
ppszRetVal[dwCtr] = AllocADsStr(ppszStr[dwCtr]);
|
|
|
|
if (!ppszRetVal[dwCtr]) {
|
|
BAIL_ON_FAILURE(E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
}
|
|
|
|
return ppszRetVal;
|
|
|
|
error:
|
|
|
|
if (ppszRetVal) {
|
|
for (DWORD i = 0; i < dwCtr; i++) {
|
|
if (ppszRetVal[i]) {
|
|
FreeADsStr(ppszRetVal[i]);
|
|
}
|
|
}
|
|
FreeADsMem(ppszRetVal);
|
|
ppszRetVal = NULL;
|
|
}
|
|
|
|
//
|
|
// Null from this routine means there was a failure.
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
SchemaInit(
|
|
VOID
|
|
)
|
|
{
|
|
InitializeCriticalSection( &g_SchemaCritSect );
|
|
InitializeCriticalSection(&g_SubSchemaCritSect);
|
|
InitializeCriticalSection(&g_DefaultSchemaCritSect);
|
|
}
|
|
|
|
VOID
|
|
SchemaCleanup(
|
|
VOID
|
|
)
|
|
{
|
|
SCHEMAINFO *pList = g_pSchemaInfoList;
|
|
|
|
while ( pList )
|
|
{
|
|
SCHEMAINFO *pNext = pList->Next;
|
|
|
|
delete pList;
|
|
pList = pNext;
|
|
}
|
|
|
|
delete g_pDefaultSchemaInfo;
|
|
|
|
|
|
//
|
|
// Delete the schema list containing the server infos
|
|
//
|
|
|
|
PSCHEMALIST pSubSchemaList = gpSubSchemaList;
|
|
|
|
while ( pSubSchemaList )
|
|
{
|
|
PSCHEMALIST pNext = pSubSchemaList->pNext;
|
|
|
|
if ( pSubSchemaList->pszLDAPServer )
|
|
FreeADsStr( pSubSchemaList->pszLDAPServer );
|
|
|
|
if ( pSubSchemaList->pszSubSchemaEntry )
|
|
FreeADsStr( pSubSchemaList->pszSubSchemaEntry );
|
|
|
|
FreeADsMem( pSubSchemaList );
|
|
|
|
pSubSchemaList = pNext;
|
|
}
|
|
|
|
//
|
|
// Delete critsects initialized in SchemaInit
|
|
//
|
|
DeleteCriticalSection(&g_SchemaCritSect);
|
|
DeleteCriticalSection(&g_SubSchemaCritSect);
|
|
DeleteCriticalSection(&g_DefaultSchemaCritSect);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
LdapGetSchema(
|
|
LPTSTR pszLDAPServer,
|
|
SCHEMAINFO **ppSchemaInfo,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPTSTR pszTemp = NULL;
|
|
|
|
SCHEMAINFO *pList = NULL;
|
|
SCHEMAINFO *pPrev = NULL;
|
|
|
|
LPTSTR pszTimeReg = NULL;
|
|
LPTSTR pszTimeDS = NULL;
|
|
BOOL fNotCurrent = FALSE;
|
|
WCHAR szDomainDnsName[MAX_PATH];
|
|
|
|
*ppSchemaInfo = NULL;
|
|
|
|
DWORD nCount =0;
|
|
|
|
LPWSTR pszSubSchemaEntry = NULL;
|
|
|
|
BOOL fBoundOk = FALSE; // has once bound to domain okay?
|
|
BOOL fReuseSchema = FALSE;
|
|
BOOL fTalktoAD = FALSE;
|
|
|
|
|
|
//
|
|
// In the case of a serverless path, we want to substitute the name
|
|
// of the domain for the serverName. This is because we can get more
|
|
// than one file called default.sch if a person logs on from different
|
|
// forests on to the same domain.
|
|
//
|
|
if (!pszLDAPServer) {
|
|
WCHAR szServerName[MAX_PATH];
|
|
DWORD dwErr;
|
|
|
|
dwErr = GetDefaultServer(
|
|
dwPort,
|
|
FALSE, // do not force verify
|
|
szDomainDnsName,
|
|
szServerName,
|
|
!(Credentials.GetAuthFlags() & ADS_READONLY_SERVER)
|
|
? TRUE : FALSE
|
|
);
|
|
if (dwErr == NO_ERROR) {
|
|
//
|
|
// Use the domainName returned.
|
|
//
|
|
pszLDAPServer = szDomainDnsName;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if the server uses default schema and return the schema info
|
|
//
|
|
|
|
hr = Credentials.GetUserName(&pszTemp);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
|
|
ENTER_SCHEMA_CRITSECT();
|
|
|
|
pList = g_pSchemaInfoList;
|
|
pPrev = NULL;
|
|
|
|
|
|
while ( pList )
|
|
{
|
|
//
|
|
// Checking for Schemas can now use NULL and NULL
|
|
//
|
|
|
|
//
|
|
// If the server is equivalent, and we've cached it as using
|
|
// a default (V2) schema, then we want to immediately return
|
|
// that cached schema, UNLESS (1) the server in question
|
|
// appeared to be a V3 server when we tried to retrieve the schema
|
|
// (i.e., it had a rootDSE with a subschemasubentry), AND (2)
|
|
// we're currently using different user credentials then when
|
|
// we cached the server schema. This is because we might be going
|
|
// against a V3 server that has security restrictions on its schema.
|
|
// If we previously tried to read the schema, but didn't have
|
|
// sufficient access permissions to do so, we would have defaulted
|
|
// to treating it as a v2 schema. Now, if we're using different
|
|
// credentials, we try again, in case we now have sufficient
|
|
// access permissions to read the schema.
|
|
//
|
|
if (EquivalentServers(pList->pszServerName, pszLDAPServer)) {
|
|
|
|
if ( pList->fDefaultSchema &&
|
|
!(pList->fAppearsV3 &&
|
|
!EquivalentUsers(pszTemp, pList->pszUserName)
|
|
)
|
|
)
|
|
{
|
|
*ppSchemaInfo = pList;
|
|
(*ppSchemaInfo)->AddRef();
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
goto cleanup;
|
|
}
|
|
else if (pList->fDefaultSchema &&
|
|
pList->fAppearsV3 &&
|
|
!EquivalentUsers(pszTemp, pList->pszUserName))
|
|
{
|
|
//
|
|
// Dump the cached schema in preparation for reading
|
|
// it again.
|
|
//
|
|
if ( pList->IsRefCountZero())
|
|
{
|
|
if ( pPrev == NULL )
|
|
g_pSchemaInfoList = pList->Next;
|
|
else
|
|
pPrev->Next = pList->Next;
|
|
|
|
delete pList;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pPrev = pList;
|
|
pList = pList->Next;
|
|
}
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
|
|
//
|
|
// Read the schema path from the root of the DS
|
|
//
|
|
|
|
hr = ReadSubSchemaSubEntry(
|
|
pszLDAPServer,
|
|
&pszSubSchemaEntry,
|
|
&fBoundOk,
|
|
Credentials,
|
|
dwPort
|
|
) ;
|
|
|
|
if ( SUCCEEDED(hr)) // pszSubSchemaEntry!=NULL if hr = S_OK. Checked.
|
|
{
|
|
|
|
ENTER_SCHEMA_CRITSECT();
|
|
|
|
pPrev = NULL;
|
|
pList = g_pSchemaInfoList;
|
|
while ( pList )
|
|
{
|
|
|
|
hr = ReadServerSupportsIsADControl(pszLDAPServer, &fTalktoAD, Credentials, dwPort);
|
|
if (FAILED(hr)) {
|
|
//
|
|
// Assume it is not AD and continue, there is no
|
|
// good reason for this to fail on AD.
|
|
//
|
|
fTalktoAD = FALSE;
|
|
}
|
|
|
|
if(fTalktoAD) {
|
|
// we talking to the server with AD, so then we don't have to compare the servername
|
|
fReuseSchema = EquivalentServers(pList->pszSubSchemaSubEntry, pszSubSchemaEntry );
|
|
}
|
|
else
|
|
{
|
|
// otherwise, we need to compare the server name
|
|
fReuseSchema = EquivalentServers(pList->pszServerName, pszLDAPServer) &&
|
|
EquivalentServers(pList->pszSubSchemaSubEntry, pszSubSchemaEntry );
|
|
}
|
|
|
|
if ( fReuseSchema )
|
|
{
|
|
if ( pList->IsObsolete())
|
|
{
|
|
hr = GetSchemaInfoTime(
|
|
pszLDAPServer,
|
|
pszSubSchemaEntry,
|
|
&pszTimeReg,
|
|
&pszTimeDS,
|
|
Credentials,
|
|
dwPort );
|
|
|
|
if ( FAILED(hr))
|
|
{
|
|
// Cannot get the time, assume the cache is not
|
|
// current and read again.
|
|
|
|
fNotCurrent = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If the servers are not the same, then we should
|
|
// not comparet the times. This is because
|
|
// each server has a ModifyTimeStamp that is not
|
|
// based on its update time not that of the domain.
|
|
// Note that at this point we know that the
|
|
// subSchemaSubEntry is the same.
|
|
//
|
|
if (!EquivalentServers(
|
|
pList->pszServerName,
|
|
pszLDAPServer
|
|
)
|
|
) {
|
|
fNotCurrent = TRUE;
|
|
break;
|
|
}
|
|
// Compare the time to see if we need to read
|
|
// the schema info from the file or from the DS
|
|
|
|
if ( CompareUTCTime( pList->pszTime, pszTimeReg ) >= 0 )
|
|
{
|
|
if ( CompareUTCTime( pszTimeReg, pszTimeDS ) < 0 )
|
|
{
|
|
fNotCurrent = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The schema in memory is not as current as the
|
|
// the one stored in the registry, hence, we
|
|
// need to read it anyway.
|
|
fNotCurrent = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pList->MakeCurrent();
|
|
}
|
|
|
|
*ppSchemaInfo = pList;
|
|
(*ppSchemaInfo)->AddRef();
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
goto cleanup;
|
|
}
|
|
|
|
pPrev = pList;
|
|
pList = pList->Next;
|
|
}
|
|
|
|
if ( fNotCurrent && pList != NULL )
|
|
{
|
|
if ( pList->IsRefCountZero())
|
|
{
|
|
SCHEMAINFO *pDelete = pList;
|
|
|
|
if ( pPrev == NULL )
|
|
g_pSchemaInfoList = pDelete->Next;
|
|
else
|
|
pPrev->Next = pDelete->Next;
|
|
|
|
delete pDelete;
|
|
}
|
|
|
|
pList = NULL;
|
|
}
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
|
|
// pList should be NULL at this point
|
|
|
|
hr = LdapReadSchemaInfoFromServer(
|
|
pszLDAPServer,
|
|
pszSubSchemaEntry, // SubSchemaSubEntry
|
|
pszTimeReg,
|
|
pszTimeDS,
|
|
ppSchemaInfo,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
ENTER_SCHEMA_CRITSECT();
|
|
|
|
(*ppSchemaInfo)->Next = g_pSchemaInfoList;
|
|
g_pSchemaInfoList = *ppSchemaInfo;
|
|
(*ppSchemaInfo)->AddRef();
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
}
|
|
else {
|
|
|
|
//
|
|
// There was some problem in reading from the DS. If it was
|
|
// because of some error like the attributes were not
|
|
// obtained or were not of the proper form, we will fall
|
|
// back to the default schema
|
|
//
|
|
hr = LdapReadDefaultSchema(pszLDAPServer, Credentials, ppSchemaInfo);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
//
|
|
// We leave fAppearsV3 == TRUE because this server has a
|
|
// subschemasubentry --- it's just that we can't read the
|
|
// schema (e.g., maybe we don't have permission)
|
|
//
|
|
|
|
ENTER_SCHEMA_CRITSECT();
|
|
|
|
(*ppSchemaInfo)->Next = g_pSchemaInfoList;
|
|
g_pSchemaInfoList = *ppSchemaInfo;
|
|
(*ppSchemaInfo)->AddRef();
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
}
|
|
|
|
} // end of if read of subSchemaSubEntry succeeded
|
|
else if ( fBoundOk )
|
|
{
|
|
//
|
|
// If we cannot get subschemasubentry, use default schema if
|
|
// fBoundOk; that is, we have at least
|
|
// once bound to the domain successfully before.
|
|
//
|
|
|
|
hr = LdapReadDefaultSchema( pszLDAPServer, Credentials, ppSchemaInfo );
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
(*ppSchemaInfo)->fAppearsV3 = FALSE;
|
|
|
|
ENTER_SCHEMA_CRITSECT();
|
|
|
|
(*ppSchemaInfo)->Next = g_pSchemaInfoList;
|
|
g_pSchemaInfoList = *ppSchemaInfo;
|
|
(*ppSchemaInfo)->AddRef();
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
}
|
|
|
|
else
|
|
{
|
|
|
|
//
|
|
// we cannot read subschemasubentry, but we are not using
|
|
// default schema since we have no indication that the
|
|
// we had ever bound to the domain before
|
|
//
|
|
|
|
if ( SUCCEEDED(hr)) // i.e. we could not read the schema
|
|
{
|
|
hr = E_ADS_BAD_PATHNAME;
|
|
}
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (pszSubSchemaEntry) {
|
|
FreeADsStr(pszSubSchemaEntry);
|
|
}
|
|
|
|
if ( pszTimeReg )
|
|
FreeADsMem( pszTimeReg );
|
|
|
|
if ( pszTimeDS )
|
|
FreeADsMem( pszTimeDS );
|
|
|
|
if ( pszTemp )
|
|
FreeADsStr( pszTemp );
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
LdapRemoveSchemaInfoOnServer(
|
|
LPTSTR pszLDAPPath,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort,
|
|
BOOL fForce
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SCHEMAINFO *pList = NULL;
|
|
LPWSTR pszSubSchemaSubEntry = NULL;
|
|
BOOL fBoundOk = FALSE;
|
|
|
|
//
|
|
// Read the subschemaSubEntry only once.
|
|
//
|
|
hr = ReadSubSchemaSubEntry(
|
|
pszLDAPPath,
|
|
&pszSubSchemaSubEntry,
|
|
&fBoundOk,
|
|
Credentials,
|
|
dwPort
|
|
) ;
|
|
//
|
|
// If we cannot read the subSchemaSubEntry it is not a
|
|
// V3 server and we cannot refresh.
|
|
//
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
ENTER_SCHEMA_CRITSECT();
|
|
|
|
pList = g_pSchemaInfoList;
|
|
while ( pList )
|
|
{
|
|
//
|
|
// Both NULL and NULL and also check for the servers
|
|
//
|
|
|
|
if (!pList->pszServerName && !pszLDAPPath) {
|
|
|
|
pList->MakeObsolete();
|
|
|
|
if (fForce) {
|
|
//
|
|
// Will reset time to something ancient so we
|
|
// will always pick up the schema from server.
|
|
//
|
|
LPWSTR pszTempTime;
|
|
pszTempTime = AllocADsStr(L"19800719000000.0Z");
|
|
|
|
if (pszTempTime && pList->pszTime) {
|
|
FreeADsStr(pList->pszTime);
|
|
pList->pszTime = pszTempTime;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// The match at this point has to be made based on the
|
|
// subschemaSubEntry and not on the server names.
|
|
//
|
|
|
|
|
|
if (EquivalentServers(
|
|
pList->pszSubSchemaSubEntry,
|
|
pszSubSchemaSubEntry
|
|
)
|
|
)
|
|
{
|
|
pList->MakeObsolete();
|
|
|
|
if (fForce) {
|
|
//
|
|
// Will reset time to something ancient so we
|
|
// will always pick up the schema from server.
|
|
//
|
|
LPWSTR pszTempTime;
|
|
pszTempTime = AllocADsStr(L"19800719000000.0Z");
|
|
|
|
if (pszTempTime && pList->pszTime) {
|
|
FreeADsStr(pList->pszTime);
|
|
pList->pszTime = pszTempTime;
|
|
}
|
|
}
|
|
|
|
}
|
|
} // the server name is not NULL
|
|
pList = pList->Next;
|
|
}
|
|
|
|
LEAVE_SCHEMA_CRITSECT();
|
|
|
|
error :
|
|
|
|
if (pszSubSchemaSubEntry) {
|
|
FreeADsStr(pszSubSchemaSubEntry);
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
GetSchemaInfoTime(
|
|
LPTSTR pszLDAPServer,
|
|
LPTSTR pszSubSchemaSubEntry,
|
|
LPTSTR *ppszTimeReg,
|
|
LPTSTR *ppszTimeDS,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwStatus = NO_ERROR;
|
|
LPTSTR pszLDAPPath = NULL;
|
|
LPTSTR pszRegPath = NULL;
|
|
|
|
LPTSTR *aValues = NULL;
|
|
int nCount = 0;
|
|
|
|
TCHAR szTimeReg[64];
|
|
HKEY hKey = NULL;
|
|
DWORD dwLength;
|
|
DWORD dwType;
|
|
|
|
//
|
|
// Read the schema timestamp on the DS server
|
|
//
|
|
|
|
hr = LdapReadAttribute2(
|
|
pszLDAPServer,
|
|
NULL,
|
|
pszSubSchemaSubEntry,
|
|
TEXT("modifyTimeStamp"),
|
|
&aValues,
|
|
&nCount,
|
|
Credentials,
|
|
dwPort,
|
|
L"(objectClass=subschema)"
|
|
);
|
|
if (nCount==0) {
|
|
|
|
//
|
|
// cannot get to time stamp or get to a time stamp with no values:
|
|
// both treat as E_FAIL
|
|
//
|
|
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
ADsAssert( nCount == 1 );
|
|
|
|
*ppszTimeDS = AllocADsStr( aValues[0] );
|
|
LdapValueFree( aValues );
|
|
|
|
if ( *ppszTimeDS == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
//
|
|
// See if we can find the schema info in the registry
|
|
//
|
|
|
|
pszRegPath = (LPTSTR) AllocADsMem( (_tcslen(ADSI_LDAP_KEY) +
|
|
_tcslen(pszSubSchemaSubEntry) +
|
|
2 ) * sizeof(TCHAR)); // includes "\\"
|
|
|
|
if ( pszRegPath == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
_tcscpy( pszRegPath, ADSI_LDAP_KEY );
|
|
_tcscat( pszRegPath, TEXT("\\"));
|
|
_tcscat( pszRegPath, pszSubSchemaSubEntry );
|
|
|
|
dwStatus = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
pszRegPath,
|
|
0,
|
|
KEY_READ,
|
|
&hKey
|
|
);
|
|
|
|
if ( dwStatus != NO_ERROR )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwStatus);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
//
|
|
// Read the time stamp of the schema in registry.
|
|
//
|
|
|
|
dwLength = sizeof(szTimeReg);
|
|
|
|
dwStatus = RegQueryValueEx( hKey,
|
|
SCHEMA_TIME,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) szTimeReg,
|
|
&dwLength );
|
|
|
|
if ( dwStatus )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(dwStatus);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
else
|
|
{
|
|
*ppszTimeReg = AllocADsStr( szTimeReg );
|
|
|
|
if ( *ppszTimeReg == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if ( hKey )
|
|
RegCloseKey( hKey );
|
|
|
|
if ( pszLDAPPath != NULL )
|
|
FreeADsStr( pszLDAPPath );
|
|
|
|
if ( pszRegPath != NULL )
|
|
FreeADsStr( pszRegPath );
|
|
|
|
if ( FAILED(hr))
|
|
{
|
|
if ( *ppszTimeDS )
|
|
{
|
|
FreeADsMem( *ppszTimeDS );
|
|
*ppszTimeDS = NULL;
|
|
}
|
|
|
|
if ( *ppszTimeReg )
|
|
{
|
|
FreeADsMem( *ppszTimeReg );
|
|
*ppszTimeReg = NULL;
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
LdapReadSchemaInfoFromServer(
|
|
LPTSTR pszLDAPServer,
|
|
LPTSTR pszSubSchemaSubEntry,
|
|
LPTSTR pszTimeReg,
|
|
LPTSTR pszTimeDS,
|
|
SCHEMAINFO **ppSchemaInfo,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwStatus = NO_ERROR;
|
|
LPTSTR pszRegPath = NULL;
|
|
SCHEMAINFO *pSchemaInfo = NULL;
|
|
|
|
LPTSTR *aValues = NULL;
|
|
int nCount = 0;
|
|
TCHAR szTimeReg[64];
|
|
|
|
|
|
LPWSTR aStrings[4] = { L"attributeTypes",
|
|
L"objectClasses",
|
|
L"ditContentRules",
|
|
NULL
|
|
};
|
|
|
|
LPWSTR szNTFilter = L"objectClass=*";
|
|
LPWSTR szGenericFilter = L"objectClass=subSchema";
|
|
BOOL fNTDS = FALSE;
|
|
DWORD dwSecDescType = 0;
|
|
|
|
LPTSTR *aValuesAttribTypes = NULL;
|
|
int nCountAttribTypes = 0;
|
|
LPTSTR *aValuesObjClasses = NULL;
|
|
int nCountObjClasses = 0;
|
|
LPTSTR *aValuesRules = NULL;
|
|
int nCountRules = 0;
|
|
LPBYTE Buffer = NULL;
|
|
|
|
HKEY hKeySchema = NULL;
|
|
HKEY hKey = NULL;
|
|
DWORD dwDisposition;
|
|
BOOL fReadFromDS = TRUE;
|
|
BOOL fProcessAUX = FALSE;
|
|
DWORD dwRegPathLen = 0;
|
|
|
|
*ppSchemaInfo = NULL;
|
|
|
|
DWORD dwRegAUXType = REG_DWORD;
|
|
DWORD dwRegProcessAUX = 0;
|
|
DWORD dwRegLength = sizeof(dwRegProcessAUX);
|
|
|
|
//
|
|
// Allocate an entry for the schema info that we are going to read
|
|
//
|
|
|
|
#if DBG
|
|
|
|
static BOOL fSchemaRead = FALSE;
|
|
static BOOL fGoSchemaLess = FALSE;
|
|
WCHAR pszRegPathDbg[MAX_PATH];
|
|
DWORD dwType = 0;
|
|
DWORD dwRetVal = 0;
|
|
DWORD dwLength = 0;
|
|
|
|
if (!fSchemaRead) {
|
|
|
|
_tcscpy( pszRegPathDbg, ADSI_LDAP_KEY );
|
|
_tcscat( pszRegPathDbg, TEXT("\\"));
|
|
_tcscat( pszRegPathDbg, TEXT("DBGSchema"));
|
|
//DebugDisabled
|
|
// If DBG, try and read the schema key and return
|
|
// value if that is set to 1.
|
|
dwStatus = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
pszRegPathDbg,
|
|
0,
|
|
KEY_READ,
|
|
&hKeySchema
|
|
);
|
|
|
|
if (dwStatus != NO_ERROR) {
|
|
// Do not want to keep coming back to this.
|
|
fSchemaRead = TRUE;
|
|
} else {
|
|
|
|
dwLength = sizeof(DWORD);
|
|
// Read the value of the DWORD DebugDisabled
|
|
dwStatus = RegQueryValueEx(
|
|
hKeySchema,
|
|
L"DebugDisabled",
|
|
0,
|
|
&dwType,
|
|
(LPBYTE) &dwRetVal,
|
|
&dwLength
|
|
);
|
|
|
|
if (dwStatus != NO_ERROR) {
|
|
fSchemaRead = TRUE;
|
|
} else {
|
|
// Look at the value and proceed
|
|
if (dwRetVal == 0) {
|
|
fGoSchemaLess = TRUE;
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
} // else - we were able to read the DebugDisabled key
|
|
} // else - we were able to open the key
|
|
} // if fSchemaRead
|
|
|
|
if ( hKeySchema )
|
|
RegCloseKey( hKeySchema );
|
|
|
|
// hr will be set only if we have schema disabled.
|
|
// Note that hr is initialised to S_OK so default case
|
|
// will fall through
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
#endif
|
|
|
|
pSchemaInfo = new SCHEMAINFO;
|
|
if ( pSchemaInfo == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
memset(pSchemaInfo, 0, sizeof(SCHEMAINFO));
|
|
|
|
//
|
|
// Store the server name
|
|
//
|
|
|
|
|
|
if (pszLDAPServer) {
|
|
pSchemaInfo->pszServerName = AllocADsStr( pszLDAPServer );
|
|
if ( pSchemaInfo->pszServerName == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store the name of the user under whose credentials
|
|
// we're reading the schema
|
|
//
|
|
hr = Credentials.GetUserName(&(pSchemaInfo->pszUserName));
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
|
|
//
|
|
// Store the subSchemaSubEntry path
|
|
//
|
|
|
|
pSchemaInfo->pszSubSchemaSubEntry = AllocADsStr( pszSubSchemaSubEntry );
|
|
if ( pSchemaInfo->pszSubSchemaSubEntry == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
//
|
|
// Try and see if this is NTDS or not to optimize schema calls.
|
|
// This is very likely to be satisfied from our cache as we would
|
|
// have already read the RootDSE at this point.
|
|
//
|
|
hr = ReadSecurityDescriptorControlType(
|
|
pszLDAPServer,
|
|
&dwSecDescType,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
|
|
if (SUCCEEDED(hr) && (dwSecDescType == ADSI_LDAPC_SECDESC_NT))
|
|
fNTDS = TRUE;
|
|
|
|
if ( pszTimeDS == NULL )
|
|
{
|
|
|
|
hr = LdapReadAttribute2(
|
|
pszLDAPServer,
|
|
NULL,
|
|
pszSubSchemaSubEntry,
|
|
TEXT("modifyTimeStamp"),
|
|
&aValues,
|
|
&nCount,
|
|
Credentials,
|
|
dwPort,
|
|
fNTDS ? szNTFilter : szGenericFilter
|
|
);
|
|
|
|
if (FAILED(hr) || nCount==0)
|
|
{
|
|
//
|
|
// cannot read modifyTimeStamp or modifyTimeStamp has no values:
|
|
// - treat as same
|
|
//
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
ADsAssert( nCount == 1 );
|
|
|
|
pSchemaInfo->pszTime = AllocADsStr( aValues[0] );
|
|
LdapValueFree( aValues );
|
|
|
|
if ( pSchemaInfo->pszTime == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSchemaInfo->pszTime = AllocADsStr( pszTimeDS );
|
|
|
|
if ( pSchemaInfo->pszTime == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if we can find the schema info in the registry
|
|
//
|
|
dwRegPathLen = _tcslen(ADSI_LDAP_KEY)
|
|
+ _tcslen(pszSubSchemaSubEntry)
|
|
+ (pszLDAPServer ? _tcslen(pszLDAPServer) : 0)
|
|
+ 3; // includes "\\" and . for serverName
|
|
|
|
pszRegPath = (LPTSTR) AllocADsMem( dwRegPathLen * sizeof(TCHAR));
|
|
|
|
if ( pszRegPath == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
_tcscpy( pszRegPath, ADSI_LDAP_KEY );
|
|
_tcscat( pszRegPath, TEXT("\\"));
|
|
_tcscat( pszRegPath, pszSubSchemaSubEntry );
|
|
|
|
//
|
|
// If the server is not NTDS, and it has the subSchemaSubEntry cn=Schema,
|
|
// to avoid schema key conflicts, we will add .ServerName to the key.
|
|
//
|
|
if (!fNTDS
|
|
&& pszSubSchemaSubEntry
|
|
&& pszLDAPServer // should alwasy be true
|
|
&& !_tcsicmp(pszSubSchemaSubEntry, TEXT("cn=Schema"))
|
|
) {
|
|
_tcscat( pszRegPath, TEXT("."));
|
|
_tcscat( pszRegPath, pszLDAPServer);
|
|
}
|
|
|
|
dwStatus = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
|
|
pszRegPath,
|
|
0,
|
|
TEXT(""),
|
|
REG_OPTION_NON_VOLATILE, // or volatile
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hKey,
|
|
&dwDisposition
|
|
);
|
|
|
|
if (dwStatus == NO_ERROR) {
|
|
|
|
if ( ( dwDisposition == REG_OPENED_EXISTING_KEY )
|
|
&& ( pSchemaInfo->pszTime != NULL )
|
|
&& ( pszTimeReg == NULL )
|
|
)
|
|
{
|
|
//
|
|
// Read the time stamp of the schema in cache and the time stamp
|
|
// of the schema on the server. If the time stamp on the server is
|
|
// newer, then we need to read the info from the server. Else
|
|
// the info in the cache is current and hence don't need to read
|
|
// it again.
|
|
//
|
|
|
|
DWORD dwLength = sizeof(szTimeReg);
|
|
DWORD dwType;
|
|
|
|
|
|
dwStatus = RegQueryValueEx( hKey,
|
|
SCHEMA_TIME,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) szTimeReg,
|
|
&dwLength );
|
|
|
|
if ( dwStatus )
|
|
{
|
|
dwStatus = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
// Compare the two time
|
|
if ( CompareUTCTime( szTimeReg, pSchemaInfo->pszTime ) >= 0 )
|
|
fReadFromDS = FALSE;
|
|
}
|
|
}
|
|
else if ( ( pSchemaInfo->pszTime != NULL ) && ( pszTimeReg != NULL ))
|
|
{
|
|
if ( CompareUTCTime( pszTimeReg, pSchemaInfo->pszTime ) >= 0 )
|
|
fReadFromDS = FALSE;
|
|
}
|
|
|
|
}else {
|
|
|
|
fReadFromDS = TRUE;
|
|
|
|
}
|
|
|
|
|
|
if ( !fReadFromDS )
|
|
{
|
|
//
|
|
// Read from registry, if we failed to read from the registry,
|
|
// then read it from the DS.
|
|
//
|
|
|
|
//
|
|
// We can av while reading bad info from a file
|
|
// or while processing it
|
|
//
|
|
__try {
|
|
|
|
dwStatus = ReadSchemaInfoFromRegistry(
|
|
hKey,
|
|
pszLDAPServer,
|
|
&aValuesAttribTypes,
|
|
&nCountAttribTypes,
|
|
&aValuesObjClasses,
|
|
&nCountObjClasses,
|
|
&aValuesRules,
|
|
&nCountRules,
|
|
&Buffer
|
|
);
|
|
|
|
if ( dwStatus == NO_ERROR)
|
|
{
|
|
//
|
|
// At this stage we need to try and process the info
|
|
// we got from the file. There is always a chance that
|
|
// the read was successful but the schema data is bad
|
|
//
|
|
|
|
//
|
|
// First we need to read from the registry to find whether we need to process
|
|
// AUX class or not.
|
|
//
|
|
|
|
dwStatus = RegQueryValueExW( hKey,
|
|
SCHEMA_PROCESSAUX,
|
|
NULL,
|
|
&dwRegAUXType,
|
|
(LPBYTE) &dwRegProcessAUX,
|
|
&dwRegLength);
|
|
|
|
if(ERROR_SUCCESS == dwStatus) {
|
|
|
|
fProcessAUX = (BOOL) dwRegProcessAUX;
|
|
|
|
hr = ProcessSchemaInfo(
|
|
pSchemaInfo,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules,
|
|
fProcessAUX
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
dwStatus = GetExceptionCode();
|
|
|
|
if (dwStatus != EXCEPTION_ACCESS_VIOLATION) {
|
|
ADsDebugOut((DEB_ERROR, "Processing Schema Info:Unknown Exception %d\n", dwStatus));
|
|
}
|
|
|
|
hr = E_FAIL;
|
|
|
|
} // end of exception handler
|
|
|
|
|
|
if (FAILED(hr) || dwStatus) {
|
|
//
|
|
// We can read the schema from the ds and upgrade our
|
|
// local copy to get rid of the bad file
|
|
//
|
|
fReadFromDS = TRUE;
|
|
|
|
//
|
|
// Need to cleanup here so that we wont leak mem.
|
|
//
|
|
if ( aValuesAttribTypes ){
|
|
FreeADsMem( aValuesAttribTypes );
|
|
aValuesAttribTypes = NULL;
|
|
}
|
|
|
|
if ( aValuesObjClasses ) {
|
|
FreeADsMem( aValuesObjClasses );
|
|
aValuesObjClasses = NULL;
|
|
}
|
|
|
|
if ( aValuesRules ) {
|
|
FreeADsMem( aValuesRules );
|
|
aValuesRules = NULL;
|
|
}
|
|
|
|
if ( Buffer ) {
|
|
FreeADsMem( Buffer );
|
|
Buffer = NULL;
|
|
}
|
|
|
|
hr = E_FAIL;
|
|
fReadFromDS = TRUE;
|
|
}
|
|
|
|
|
|
} // if !fReadFromDS
|
|
|
|
if ( fReadFromDS )
|
|
{
|
|
|
|
//
|
|
// At this point, the info in the DS is newer or we have failed
|
|
// to read the info from the registry, hence we need to read
|
|
// from the DS and then store it in the registry.
|
|
//
|
|
|
|
//
|
|
// As per the LDAP spec if the server does not know about
|
|
// an attribute then it will ignore the attribute. So it should
|
|
// be ok to ask for the ditContentRules even though the server
|
|
// may not know about them.
|
|
//
|
|
hr = HelperReadLDAPSchemaInfo(
|
|
pszLDAPServer,
|
|
pszSubSchemaSubEntry,
|
|
aStrings,
|
|
fNTDS ? szNTFilter : szGenericFilter,
|
|
&aValuesAttribTypes,
|
|
&aValuesObjClasses,
|
|
&aValuesRules,
|
|
&nCountAttribTypes,
|
|
&nCountObjClasses,
|
|
&nCountRules,
|
|
Credentials,
|
|
dwPort);
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (nCountAttribTypes == 0 || nCountObjClasses == 0) {
|
|
BAIL_IF_ERROR(hr = E_FAIL);
|
|
}
|
|
|
|
|
|
//
|
|
// We need to know if we need to process the aux classes
|
|
// or not at this stage. If the server is enhanced AD (build 2220+),
|
|
// we should not. Also if it is anything other than AD on Win2k we should
|
|
// not as we will end up interpreting the schema incorrectly.
|
|
//
|
|
BOOL fLaterThanAD, fAD;
|
|
hr = ReadServerSupportsIsEnhancedAD(
|
|
pszLDAPServer,
|
|
&fLaterThanAD,
|
|
&fAD,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
if (FAILED(hr)) {
|
|
//
|
|
// We will not process the aux classes.
|
|
//
|
|
fProcessAUX = FALSE;
|
|
}
|
|
|
|
if (fLaterThanAD) {
|
|
fProcessAUX = FALSE;
|
|
}
|
|
else if (!fLaterThanAD && fAD) {
|
|
fProcessAUX = TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// This is not expected to AV as this is info from the
|
|
// server that is why it is not in a try except block
|
|
//
|
|
hr = ProcessSchemaInfo(
|
|
pSchemaInfo,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules,
|
|
fProcessAUX
|
|
);
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
} // if fReadFromDS
|
|
|
|
//
|
|
// Store all the info in the registry only if the time stamp
|
|
// is present and we have read just read it from the server.
|
|
// Ignore the error since if we failed to store it, we can
|
|
// still read it from the DS.
|
|
//
|
|
|
|
if ( fReadFromDS && pSchemaInfo->pszTime )
|
|
{
|
|
StoreSchemaInfoInRegistry( hKey,
|
|
pszLDAPServer,
|
|
pSchemaInfo->pszTime,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules,
|
|
fProcessAUX);
|
|
}
|
|
|
|
*ppSchemaInfo = pSchemaInfo;
|
|
|
|
cleanup:
|
|
|
|
if ( fReadFromDS )
|
|
{
|
|
if ( aValuesAttribTypes )
|
|
LdapValueFree( aValuesAttribTypes );
|
|
|
|
if ( aValuesObjClasses )
|
|
LdapValueFree( aValuesObjClasses );
|
|
|
|
if ( aValuesRules )
|
|
LdapValueFree( aValuesRules );
|
|
|
|
}
|
|
else
|
|
{
|
|
if ( aValuesAttribTypes )
|
|
FreeADsMem( aValuesAttribTypes );
|
|
|
|
if ( aValuesObjClasses )
|
|
FreeADsMem( aValuesObjClasses );
|
|
|
|
if ( aValuesRules )
|
|
FreeADsMem( aValuesRules );
|
|
|
|
if ( Buffer )
|
|
FreeADsMem( Buffer );
|
|
}
|
|
|
|
if ( hKey )
|
|
RegCloseKey( hKey );
|
|
|
|
if ( pszRegPath != NULL )
|
|
FreeADsStr( pszRegPath );
|
|
|
|
if ( FAILED(hr) && pSchemaInfo )
|
|
delete pSchemaInfo;
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ProcessSchemaInfo(
|
|
SCHEMAINFO *pSchemaInfo,
|
|
LPTSTR *aValuesAttribTypes,
|
|
DWORD dwAttribCount,
|
|
LPTSTR *aValuesObjClasses,
|
|
DWORD dwObjClassesCount,
|
|
LPTSTR *aValuesRules,
|
|
DWORD dwRulesCount,
|
|
BOOL fProcessAUX
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
hr = FillPropertyInfoArray(
|
|
aValuesAttribTypes,
|
|
dwAttribCount,
|
|
&(pSchemaInfo->aProperties),
|
|
&(pSchemaInfo->nNumOfProperties),
|
|
&(pSchemaInfo->aPropertiesSearchTable)
|
|
);
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
hr = FillClassInfoArray(
|
|
aValuesObjClasses,
|
|
dwObjClassesCount,
|
|
pSchemaInfo->aPropertiesSearchTable,
|
|
pSchemaInfo->nNumOfProperties * 2,
|
|
&(pSchemaInfo->aClasses),
|
|
&(pSchemaInfo->nNumOfClasses),
|
|
&(pSchemaInfo->aClassesSearchTable)
|
|
);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( aValuesRules )
|
|
{
|
|
hr = FillAuxClassInfoArray(
|
|
aValuesRules,
|
|
dwRulesCount,
|
|
pSchemaInfo->aPropertiesSearchTable,
|
|
pSchemaInfo->nNumOfProperties * 2,
|
|
pSchemaInfo->aClasses,
|
|
pSchemaInfo->nNumOfClasses,
|
|
pSchemaInfo->aClassesSearchTable
|
|
);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
//
|
|
// fProcssAUX tells us if we need to add the list of must
|
|
// contain on each of the classes in the AUX list to the appopriate
|
|
// classes list. Say :
|
|
// 1.2.3.4 NAME 'OrganizationalUnit' AUX ($Class1 $CLASS2) MUST (List)
|
|
// May (List). Then if the flag is true, we will add the Must and May
|
|
// of class1 and class2 to the must and may of class OrganizationalUnit
|
|
// (the must and may list is always processed - they are lists
|
|
// of attributes).
|
|
//
|
|
hr = ProcessClassInfoArray(
|
|
pSchemaInfo->aClasses,
|
|
pSchemaInfo->nNumOfClasses,
|
|
pSchemaInfo->aClassesSearchTable,
|
|
fProcessAUX
|
|
);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
cleanup :
|
|
//
|
|
// Nothing to do for now
|
|
//
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Helper to read the schema information from subSchemaSubEntry
|
|
//
|
|
HRESULT
|
|
HelperReadLDAPSchemaInfo(
|
|
LPWSTR pszLDAPServer,
|
|
LPWSTR pszSubSchemaSubEntry,
|
|
LPWSTR szAttributes[],
|
|
LPWSTR pszFilter,
|
|
LPTSTR **aValuesAttribTypes,
|
|
LPTSTR **aValuesObjClasses,
|
|
LPTSTR **aValuesRules,
|
|
int *nCountAttributes,
|
|
int *nCountObjClasses,
|
|
int *nCountRules,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ADS_LDP *ld = NULL;
|
|
LDAPMessage *res = NULL;
|
|
LDAPMessage *e = NULL;
|
|
|
|
hr = LdapOpenObject2(
|
|
pszLDAPServer,
|
|
NULL,
|
|
pszSubSchemaSubEntry,
|
|
&ld,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
ADsAssert(ld && ld->LdapHandle);
|
|
|
|
hr = LdapSearchS(
|
|
ld,
|
|
pszSubSchemaSubEntry,
|
|
LDAP_SCOPE_BASE,
|
|
pszFilter,
|
|
szAttributes,
|
|
0,
|
|
&res
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
BAIL_ON_FAILURE((hr = LdapFirstEntry(ld, res, &e)));
|
|
|
|
hr = LdapGetValues(
|
|
ld,
|
|
e,
|
|
szAttributes[0],
|
|
aValuesAttribTypes,
|
|
nCountAttributes
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = LdapGetValues(
|
|
ld,
|
|
e,
|
|
szAttributes[1],
|
|
aValuesObjClasses,
|
|
nCountObjClasses
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
hr = LdapGetValues(
|
|
ld,
|
|
e,
|
|
szAttributes[2],
|
|
aValuesRules,
|
|
nCountRules
|
|
);
|
|
|
|
if (FAILED(hr)) {
|
|
//
|
|
// This is non critical
|
|
//
|
|
*aValuesRules = NULL;
|
|
nCountRules = 0;
|
|
hr = S_OK;
|
|
}
|
|
|
|
error:
|
|
|
|
if (res) {
|
|
LdapMsgFree(res);
|
|
}
|
|
|
|
if (ld) {
|
|
LdapCloseObject(ld);
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
LdapReadDefaultSchema(
|
|
LPTSTR pszServer,
|
|
CCredentials &Credentials,
|
|
SCHEMAINFO **ppSchemaInfo
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SCHEMAINFO *pSchemaInfo = NULL;
|
|
|
|
*ppSchemaInfo = NULL;
|
|
|
|
ENTER_DEFAULTSCHEMA_CRITSECT();
|
|
|
|
if ( g_pDefaultSchemaInfo == NULL )
|
|
{
|
|
g_pDefaultSchemaInfo = new SCHEMAINFO;
|
|
if ( g_pDefaultSchemaInfo == NULL )
|
|
{
|
|
LEAVE_DEFAULTSCHEMA_CRITSECT();
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
hr = FillPropertyInfoArray( g_aDefaultAttributeTypes,
|
|
g_cDefaultAttributeTypes,
|
|
&(g_pDefaultSchemaInfo->aProperties),
|
|
&(g_pDefaultSchemaInfo->nNumOfProperties),
|
|
&(g_pDefaultSchemaInfo->aPropertiesSearchTable));
|
|
|
|
//
|
|
// Now read the object classes from the schema
|
|
//
|
|
|
|
if ( SUCCEEDED(hr))
|
|
{
|
|
|
|
hr = FillClassInfoArray( g_aDefaultObjectClasses,
|
|
g_cDefaultObjectClasses,
|
|
g_pDefaultSchemaInfo->aPropertiesSearchTable,
|
|
g_pDefaultSchemaInfo->nNumOfProperties * 2,
|
|
&(g_pDefaultSchemaInfo->aClasses),
|
|
&(g_pDefaultSchemaInfo->nNumOfClasses),
|
|
&(g_pDefaultSchemaInfo->aClassesSearchTable));
|
|
|
|
if ( SUCCEEDED(hr))
|
|
{
|
|
hr = ProcessClassInfoArray( g_pDefaultSchemaInfo->aClasses,
|
|
g_pDefaultSchemaInfo->nNumOfClasses,
|
|
g_pDefaultSchemaInfo->aClassesSearchTable );
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
delete g_pDefaultSchemaInfo;
|
|
g_pDefaultSchemaInfo = NULL;
|
|
LEAVE_DEFAULTSCHEMA_CRITSECT();
|
|
}
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
LEAVE_DEFAULTSCHEMA_CRITSECT();
|
|
|
|
//
|
|
// Allocate an entry for the schema info
|
|
//
|
|
|
|
pSchemaInfo = new SCHEMAINFO;
|
|
if ( pSchemaInfo == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
//
|
|
// Store the server name
|
|
//
|
|
|
|
if (pszServer) {
|
|
|
|
pSchemaInfo->pszServerName = AllocADsStr( pszServer );
|
|
if ( pSchemaInfo->pszServerName == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Store the name of the user under whose credentials
|
|
// we're reading the schema
|
|
//
|
|
hr = Credentials.GetUserName(&(pSchemaInfo->pszUserName));
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
|
|
pSchemaInfo->aClasses = g_pDefaultSchemaInfo->aClasses;
|
|
pSchemaInfo->nNumOfClasses = g_pDefaultSchemaInfo->nNumOfClasses;
|
|
pSchemaInfo->aClassesSearchTable = g_pDefaultSchemaInfo->aClassesSearchTable;
|
|
pSchemaInfo->aProperties = g_pDefaultSchemaInfo->aProperties;
|
|
pSchemaInfo->nNumOfProperties = g_pDefaultSchemaInfo->nNumOfProperties;
|
|
pSchemaInfo->aPropertiesSearchTable = g_pDefaultSchemaInfo->aPropertiesSearchTable;
|
|
pSchemaInfo->fDefaultSchema = TRUE;
|
|
|
|
*ppSchemaInfo = pSchemaInfo;
|
|
|
|
cleanup:
|
|
|
|
if ( FAILED(hr) && pSchemaInfo )
|
|
delete pSchemaInfo;
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT FillPropertyInfoArray(
|
|
LPTSTR *aAttrTypes,
|
|
DWORD dwCount,
|
|
PROPERTYINFO **paProperties,
|
|
DWORD *pnProperties,
|
|
SEARCHENTRY **paPropertiesSearchTable
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD i = 0;
|
|
PROPERTYINFO * pPropArray = NULL;
|
|
PROPERTYINFO * pNewPropArray = NULL;
|
|
LPWSTR *ppszNewNames = NULL;
|
|
DWORD dwNewNameCount = 0;
|
|
DWORD dwDisplacement = 0;
|
|
BOOL fFreeNames = TRUE;
|
|
|
|
*paProperties = NULL;
|
|
*pnProperties = 0;
|
|
*paPropertiesSearchTable = NULL;
|
|
|
|
if ( dwCount == 0 )
|
|
RRETURN(S_OK);
|
|
|
|
pPropArray = (PROPERTYINFO *)AllocADsMem( sizeof(PROPERTYINFO) * dwCount);
|
|
if (!pPropArray) {
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
for ( i = 0; i < dwCount; i++) {
|
|
|
|
fFreeNames = FREE_ALL_BUT_FIRST;
|
|
dwNewNameCount = 0;
|
|
|
|
pPropArray[i].dwUsage = ATTR_USAGE_USERAPPLICATIONS;
|
|
|
|
hr = AttributeTypeDescription(
|
|
aAttrTypes[i],
|
|
pPropArray + (i+dwDisplacement),
|
|
&ppszNewNames,
|
|
&dwNewNameCount
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (ppszNewNames) {
|
|
|
|
if (dwNewNameCount > 1) {
|
|
hr = AddNewNamesToPropertyArray(
|
|
&pPropArray,
|
|
i + dwDisplacement, // current position in array
|
|
dwCount + dwDisplacement, // total number already in array
|
|
ppszNewNames, // array of names.
|
|
dwNewNameCount // number of names
|
|
);
|
|
|
|
//
|
|
// In the failure case, we can still continue, just
|
|
// that the additional names will not be recognized.
|
|
//
|
|
if (SUCCEEDED(hr)) {
|
|
//
|
|
// In this case the new array has the data needed.
|
|
// The count is updated suitably only on success.
|
|
//
|
|
fFreeNames = FALSE;
|
|
dwDisplacement += (dwNewNameCount - 1);
|
|
}
|
|
}
|
|
|
|
FreeDirectoryStrings(
|
|
ppszNewNames,
|
|
dwNewNameCount,
|
|
fFreeNames ?
|
|
FREE_ALL_BUT_FIRST : FREE_ARRAY_NOT_ELEMENTS
|
|
);
|
|
|
|
ppszNewNames = NULL; // freed in the above call.
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reduce i by one in case we fail and call FreePropertyInfoArray below.
|
|
//
|
|
i--;
|
|
dwCount += dwDisplacement;
|
|
hr = ProcessPropertyInfoArray(pPropArray, dwCount, paPropertiesSearchTable);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
*paProperties = pPropArray;
|
|
*pnProperties = dwCount;
|
|
|
|
RRETURN(S_OK);
|
|
|
|
error:
|
|
|
|
FreePropertyInfoArray( pPropArray, i + 1);
|
|
|
|
//
|
|
// Need to free the new names list if it is valid.
|
|
//
|
|
if (ppszNewNames) {
|
|
//
|
|
// This function frees pszNames too.
|
|
//
|
|
FreeDirectoryStrings(
|
|
ppszNewNames,
|
|
dwNewNameCount,
|
|
FREE_ALL_BUT_FIRST // do not free first element
|
|
);
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
GetInfoFromSuperiorProperty(
|
|
PROPERTYINFO *pPropertyInfo,
|
|
PROPERTYINFO *pPropertyInfoSuperior
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if ( pPropertyInfo->pszSyntax == NULL )
|
|
{
|
|
pPropertyInfo->pszSyntax =
|
|
AllocADsStr( pPropertyInfoSuperior->pszSyntax );
|
|
|
|
if ( pPropertyInfo->pszSyntax == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if ( pPropertyInfo->lMaxRange == 0
|
|
&& pPropertyInfo->lMinRange == 0
|
|
)
|
|
{
|
|
pPropertyInfo->lMaxRange = pPropertyInfoSuperior->lMaxRange;
|
|
pPropertyInfo->lMinRange = pPropertyInfoSuperior->lMinRange;
|
|
}
|
|
#endif
|
|
|
|
if ( pPropertyInfoSuperior->fSingleValued
|
|
&& !pPropertyInfo->fSingleValued
|
|
)
|
|
{
|
|
pPropertyInfo->fSingleValued = pPropertyInfoSuperior->fSingleValued;
|
|
}
|
|
|
|
if ( pPropertyInfo->pszOIDEquality == NULL
|
|
&& pPropertyInfoSuperior->pszOIDEquality != NULL
|
|
)
|
|
{
|
|
pPropertyInfo->pszOIDEquality =
|
|
AllocADsStr( pPropertyInfoSuperior->pszOIDEquality );
|
|
|
|
if ( pPropertyInfo->pszOIDEquality == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
if ( pPropertyInfo->pszOIDOrdering == NULL
|
|
&& pPropertyInfoSuperior->pszOIDOrdering != NULL
|
|
)
|
|
{
|
|
pPropertyInfo->pszOIDOrdering =
|
|
AllocADsStr( pPropertyInfoSuperior->pszOIDOrdering );
|
|
|
|
if ( pPropertyInfo->pszOIDOrdering == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
if ( pPropertyInfo->pszOIDSubstr == NULL
|
|
&& pPropertyInfoSuperior->pszOIDSubstr != NULL
|
|
)
|
|
{
|
|
pPropertyInfo->pszOIDSubstr =
|
|
AllocADsStr( pPropertyInfoSuperior->pszOIDSubstr );
|
|
|
|
if ( pPropertyInfo->pszOIDSubstr == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
HRESULT ProcessPropertyInfoArray(
|
|
PROPERTYINFO *aProperties,
|
|
DWORD nProperties,
|
|
SEARCHENTRY **paPropertiesSearchTable
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SEARCHENTRY *aSearchTable = NULL;
|
|
SEARCHENTRY searchEntry;
|
|
SEARCHENTRY *matchedEntry;
|
|
DWORD i;
|
|
BOOL fProcessedAll = TRUE;
|
|
DWORD nLoop = 0;
|
|
|
|
*paPropertiesSearchTable = NULL;
|
|
|
|
//
|
|
// First, build a binary search table for faster lookup
|
|
//
|
|
|
|
aSearchTable = (SEARCHENTRY *) AllocADsMem(
|
|
sizeof(SEARCHENTRY) * nProperties * 2);
|
|
|
|
if (!aSearchTable) {
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
for ( i = 0; i < nProperties; i++ )
|
|
{
|
|
// OIDs can be specified in 2.5.6.0 format or name.
|
|
// So, we need both entries in the search table.
|
|
|
|
//
|
|
// Special case to handle no names or OID's
|
|
//
|
|
if (aProperties[i].pszPropertyName == NULL) {
|
|
aProperties[i].pszPropertyName = AllocADsStr(aProperties[i].pszOID);
|
|
}
|
|
|
|
if (aProperties[i].pszOID == NULL) {
|
|
aProperties[i].pszOID = AllocADsStr(aProperties[i].pszPropertyName);
|
|
}
|
|
|
|
aSearchTable[2*i].pszName = aProperties[i].pszPropertyName;
|
|
aSearchTable[2*i].nIndex = i;
|
|
aSearchTable[2*i+1].pszName = aProperties[i].pszOID;
|
|
aSearchTable[2*i+1].nIndex = i;
|
|
}
|
|
|
|
//
|
|
// Sort the table
|
|
//
|
|
qsort( aSearchTable, 2*nProperties, sizeof(SEARCHENTRY), searchentrycmp );
|
|
|
|
|
|
do {
|
|
|
|
fProcessedAll = TRUE;
|
|
|
|
for ( DWORD i = 0; i < nProperties; i++ )
|
|
{
|
|
LPTSTR pszOIDSup = NULL;
|
|
|
|
if ( aProperties[i].fProcessedSuperiorClass )
|
|
continue;
|
|
|
|
pszOIDSup = aProperties[i].pszOIDSup;
|
|
|
|
if ( pszOIDSup == NULL )
|
|
{
|
|
aProperties[i].fProcessedSuperiorClass = TRUE;
|
|
continue;
|
|
}
|
|
|
|
searchEntry.pszName = pszOIDSup;
|
|
matchedEntry = (SEARCHENTRY *) bsearch(
|
|
(SEARCHENTRY *) &searchEntry,
|
|
aSearchTable, 2*nProperties,
|
|
sizeof(SEARCHENTRY), searchentrycmp );
|
|
|
|
if ( matchedEntry ) // found the superior class
|
|
{
|
|
if (!aProperties[matchedEntry->nIndex].fProcessedSuperiorClass)
|
|
{
|
|
fProcessedAll = FALSE;
|
|
continue;
|
|
}
|
|
|
|
hr = GetInfoFromSuperiorProperty(
|
|
&(aProperties[i]), &(aProperties[matchedEntry->nIndex]));
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
aProperties[i].fProcessedSuperiorClass = TRUE;
|
|
}
|
|
|
|
nLoop++;
|
|
|
|
} while ( (nLoop < MAX_LOOP_COUNT) && !fProcessedAll );
|
|
|
|
*paPropertiesSearchTable = aSearchTable;
|
|
RRETURN(S_OK);
|
|
|
|
error:
|
|
|
|
if ( aSearchTable )
|
|
FreeADsMem( aSearchTable );
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
VOID FreePropertyInfoArray(
|
|
PROPERTYINFO *aProperties,
|
|
DWORD nProperties
|
|
)
|
|
{
|
|
if ( aProperties )
|
|
{
|
|
DWORD j;
|
|
|
|
for ( j = 0; j < nProperties; j++ )
|
|
{
|
|
FreeADsStr( aProperties[j].pszPropertyName );
|
|
FreeADsStr( aProperties[j].pszOID );
|
|
FreeADsStr( aProperties[j].pszSyntax );
|
|
FreeADsStr( aProperties[j].pszDescription );
|
|
FreeADsStr( aProperties[j].pszOIDSup );
|
|
FreeADsStr( aProperties[j].pszOIDEquality );
|
|
FreeADsStr( aProperties[j].pszOIDOrdering );
|
|
FreeADsStr( aProperties[j].pszOIDSubstr );
|
|
}
|
|
|
|
FreeADsMem( aProperties );
|
|
}
|
|
}
|
|
|
|
HRESULT FillClassInfoArray(
|
|
LPTSTR *aObjectClasses,
|
|
DWORD dwCount,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount,
|
|
CLASSINFO **paClasses,
|
|
DWORD *pnClasses,
|
|
SEARCHENTRY **paClassesSearchTable
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD i = 0;
|
|
CLASSINFO * pClassArray = NULL;
|
|
SEARCHENTRY *aSearchTable = NULL;
|
|
LPWSTR *ppszNewNames = NULL;
|
|
DWORD dwNewNameCount = 0;
|
|
DWORD dwDisplacement = 0;
|
|
BOOL fFreeNames = FALSE;
|
|
|
|
*paClasses= NULL;
|
|
*pnClasses= 0;
|
|
*paClassesSearchTable = NULL;
|
|
|
|
if ( dwCount == 0 )
|
|
RRETURN(S_OK);
|
|
|
|
pClassArray = (CLASSINFO *)AllocADsMem( sizeof(CLASSINFO) * dwCount );
|
|
if (!pClassArray) {
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
for ( i = 0; i < dwCount; i++) {
|
|
|
|
ppszNewNames = NULL;
|
|
dwNewNameCount = 0;
|
|
fFreeNames = TRUE;
|
|
|
|
pClassArray[i].IsContainer = -1;
|
|
pClassArray[i].dwType = CLASS_TYPE_STRUCTURAL;
|
|
|
|
hr = ObjectClassDescription(
|
|
aObjectClasses[i],
|
|
pClassArray + (i + dwDisplacement),
|
|
aPropSearchTable,
|
|
dwSearchTableCount,
|
|
&ppszNewNames,
|
|
&dwNewNameCount
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (ppszNewNames) {
|
|
|
|
if (dwNewNameCount > 1) {
|
|
//
|
|
// ********************** IMPORTANT NOTE ************
|
|
// In this routine, we specifically do not duplicate
|
|
// the pCLSID and pPrimaryInterfaceGUID as these are not
|
|
// freed when we exit and do not appear to be used anywehre.
|
|
// If ObjectClasDescription is changed to add this info,
|
|
// then the fn below needs to be update accordingly.
|
|
// **************************************************
|
|
//
|
|
hr = AddNewNamesToClassArray(
|
|
&pClassArray,
|
|
i + dwDisplacement, // current position in array
|
|
dwCount + dwDisplacement, // total number already in array
|
|
ppszNewNames, // array of names.
|
|
dwNewNameCount // number of names
|
|
);
|
|
|
|
//
|
|
// In the failure case, we can still continue, just
|
|
// that the additional names will not be recognized.
|
|
//
|
|
if (SUCCEEDED(hr)) {
|
|
//
|
|
// In this case the new array has the data needed.
|
|
// The count is updated suitably only on success.
|
|
//
|
|
fFreeNames = FALSE;
|
|
dwDisplacement += (dwNewNameCount - 1);
|
|
}
|
|
}
|
|
|
|
FreeDirectoryStrings(
|
|
ppszNewNames,
|
|
dwNewNameCount,
|
|
fFreeNames ?
|
|
FREE_ALL_BUT_FIRST : FREE_ARRAY_NOT_ELEMENTS
|
|
);
|
|
|
|
ppszNewNames = NULL; // freed in the above call.
|
|
|
|
} // if newNames is valid.
|
|
|
|
} // for
|
|
|
|
//
|
|
// Count should now include the new elements added.
|
|
//
|
|
dwCount += dwDisplacement;
|
|
//
|
|
// Build a binary search table for faster lookup
|
|
//
|
|
|
|
aSearchTable = (SEARCHENTRY *) AllocADsMem(
|
|
sizeof(SEARCHENTRY) * dwCount * 2);
|
|
|
|
if (!aSearchTable) {
|
|
hr = E_OUTOFMEMORY;
|
|
//
|
|
// i is now dwCount but should be one less as
|
|
// the free call is made with i+1
|
|
//
|
|
i--;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
for ( i = 0; i < dwCount; i++ )
|
|
{
|
|
|
|
//
|
|
// Take care of the NULL name/OID in case of some servers
|
|
//
|
|
if (pClassArray[i].pszName == NULL) {
|
|
pClassArray[i].pszName = AllocADsStr(pClassArray[i].pszOID);
|
|
}
|
|
|
|
if (pClassArray[i].pszOID == NULL) {
|
|
pClassArray[i].pszOID = AllocADsStr(pClassArray[i].pszName);
|
|
}
|
|
|
|
// OIDs can be specified in 2.5.6.0 format or name.
|
|
// So, we need both entries in the search table.
|
|
|
|
aSearchTable[2*i].pszName = pClassArray[i].pszName;
|
|
aSearchTable[2*i].nIndex = i;
|
|
aSearchTable[2*i+1].pszName = pClassArray[i].pszOID;
|
|
aSearchTable[2*i+1].nIndex = i;
|
|
}
|
|
|
|
//
|
|
// Sort the table
|
|
//
|
|
qsort( aSearchTable, 2*dwCount, sizeof(SEARCHENTRY), searchentrycmp );
|
|
|
|
*paClasses = pClassArray;
|
|
*pnClasses = dwCount;
|
|
*paClassesSearchTable = aSearchTable;
|
|
|
|
RRETURN(S_OK);
|
|
|
|
error:
|
|
|
|
FreeClassInfoArray( pClassArray, i + 1 );
|
|
|
|
if ( aSearchTable )
|
|
FreeADsMem( aSearchTable );
|
|
|
|
if (ppszNewNames) {
|
|
FreeDirectoryStrings(
|
|
ppszNewNames,
|
|
dwNewNameCount,
|
|
FREE_ALL_BUT_FIRST // first taken care of above.
|
|
);
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT FillAuxClassInfoArray(
|
|
LPTSTR *aDITContentRules,
|
|
DWORD dwCount,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount,
|
|
CLASSINFO *aClasses,
|
|
DWORD nClasses,
|
|
SEARCHENTRY *aClassesSearchTable
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD i = 0;
|
|
CLASSINFO ClassInfo;
|
|
int index;
|
|
|
|
|
|
if ( dwCount == 0 )
|
|
RRETURN(S_OK);
|
|
|
|
for ( i = 0; i < dwCount; i++) {
|
|
|
|
memset( &ClassInfo, 0, sizeof(ClassInfo));
|
|
|
|
hr = DITContentRuleDescription( aDITContentRules[i], &ClassInfo,
|
|
aPropSearchTable, dwSearchTableCount );
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
index = FindEntryInSearchTable( ClassInfo.pszOID,
|
|
aClassesSearchTable,
|
|
nClasses * 2 );
|
|
|
|
if ( index == -1 )
|
|
continue; // Cannot find the class, ignore and continue
|
|
|
|
|
|
aClasses[index].pOIDsNotContain = ClassInfo.pOIDsNotContain;
|
|
aClasses[index].nNumOfNotContain = ClassInfo.nNumOfNotContain;
|
|
|
|
aClasses[index].pOIDsAuxClasses = ClassInfo.pOIDsAuxClasses;
|
|
|
|
if ( ClassInfo.pOIDsMustContain )
|
|
{
|
|
DWORD nMustContain = aClasses[index].nNumOfMustContain;
|
|
|
|
if ( nMustContain == 0 )
|
|
{
|
|
aClasses[index].pOIDsMustContain = ClassInfo.pOIDsMustContain;
|
|
aClasses[index].nNumOfMustContain = ClassInfo.nNumOfMustContain;
|
|
}
|
|
else
|
|
{
|
|
aClasses[index].pOIDsMustContain =
|
|
(int *) ReallocADsMem( aClasses[index].pOIDsMustContain,
|
|
sizeof(int) * nMustContain,
|
|
sizeof(int) * ( nMustContain +
|
|
ClassInfo.nNumOfMustContain + 1));
|
|
|
|
if ( aClasses[index].pOIDsMustContain == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
memcpy( &(aClasses[index].pOIDsMustContain[nMustContain]),
|
|
ClassInfo.pOIDsMustContain,
|
|
ClassInfo.nNumOfMustContain * sizeof(int));
|
|
|
|
aClasses[index].nNumOfMustContain += ClassInfo.nNumOfMustContain;
|
|
//
|
|
// Need to terminate with -1.
|
|
//
|
|
aClasses[index].pOIDsMustContain[nMustContain+ClassInfo.nNumOfMustContain] = -1;
|
|
|
|
FreeADsMem( ClassInfo.pOIDsMustContain );
|
|
ClassInfo.pOIDsMustContain = NULL;
|
|
}
|
|
}
|
|
|
|
if ( ClassInfo.pOIDsMayContain )
|
|
{
|
|
DWORD nMayContain = aClasses[index].nNumOfMayContain;
|
|
|
|
if ( nMayContain == 0 )
|
|
{
|
|
aClasses[index].pOIDsMayContain = ClassInfo.pOIDsMayContain;
|
|
aClasses[index].nNumOfMayContain = ClassInfo.nNumOfMayContain;
|
|
}
|
|
else
|
|
{
|
|
aClasses[index].pOIDsMayContain =
|
|
(int *) ReallocADsMem( aClasses[index].pOIDsMayContain,
|
|
sizeof(int) * nMayContain,
|
|
sizeof(int) * ( nMayContain +
|
|
ClassInfo.nNumOfMayContain + 1));
|
|
|
|
if ( aClasses[index].pOIDsMayContain == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
memcpy( &(aClasses[index].pOIDsMayContain[nMayContain]),
|
|
ClassInfo.pOIDsMayContain,
|
|
ClassInfo.nNumOfMayContain * sizeof(int));
|
|
|
|
aClasses[index].nNumOfMayContain += ClassInfo.nNumOfMayContain;
|
|
|
|
//
|
|
// Need to terminate with -1.
|
|
//
|
|
aClasses[index].pOIDsMayContain[nMayContain+ClassInfo.nNumOfMayContain] = -1;
|
|
|
|
FreeADsMem( ClassInfo.pOIDsMayContain );
|
|
ClassInfo.pOIDsMayContain = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
FreeADsStr( ClassInfo.pszOID );
|
|
ClassInfo.pszOID = NULL;
|
|
|
|
FreeADsStr( ClassInfo.pszName );
|
|
FreeADsStr( ClassInfo.pszDescription );
|
|
}
|
|
|
|
RRETURN(S_OK);
|
|
|
|
error:
|
|
|
|
if ( ClassInfo.pszOID )
|
|
{
|
|
FreeADsStr( ClassInfo.pszOID );
|
|
FreeADsStr( ClassInfo.pszName );
|
|
FreeADsStr( ClassInfo.pszDescription );
|
|
|
|
if ( ClassInfo.pOIDsMustContain )
|
|
{
|
|
FreeADsMem( ClassInfo.pOIDsMustContain );
|
|
}
|
|
|
|
if ( ClassInfo.pOIDsMayContain )
|
|
{
|
|
FreeADsMem( ClassInfo.pOIDsMayContain );
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
GetInfoFromSuperiorClasses(
|
|
CLASSINFO *pClassInfo,
|
|
CLASSINFO *pClassInfoSuperior
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD i;
|
|
|
|
|
|
int *pOIDsMustSup = pClassInfoSuperior->pOIDsMustContain;
|
|
int *pOIDsMaySup = pClassInfoSuperior->pOIDsMayContain;
|
|
DWORD nCountMustSup = pClassInfoSuperior->nNumOfMustContain;
|
|
DWORD nCountMaySup = pClassInfoSuperior->nNumOfMayContain;
|
|
|
|
int *pOIDsMust = pClassInfo->pOIDsMustContain;
|
|
int *pOIDsMay = pClassInfo->pOIDsMayContain;
|
|
DWORD nCountMust = pClassInfo->nNumOfMustContain;
|
|
DWORD nCountMay = pClassInfo->nNumOfMayContain;
|
|
|
|
int *pOIDsMustNew = NULL;
|
|
int *pOIDsMayNew = NULL;
|
|
|
|
if ( pOIDsMaySup == NULL && pOIDsMustSup == NULL )
|
|
RRETURN(S_OK);
|
|
|
|
if ( nCountMustSup > 0 )
|
|
{
|
|
pOIDsMustNew = (int *) AllocADsMem(
|
|
sizeof(int) * (nCountMust + nCountMustSup + 1));
|
|
|
|
if ( pOIDsMustNew == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
for ( i = 0; i < nCountMustSup; i++ )
|
|
{
|
|
pOIDsMustNew[i] = pOIDsMustSup[i];
|
|
}
|
|
|
|
for ( i = 0; i < nCountMust; i++ )
|
|
{
|
|
pOIDsMustNew[i+nCountMustSup] = pOIDsMust[i];
|
|
}
|
|
|
|
pOIDsMustNew[nCountMustSup+nCountMust] = -1;
|
|
|
|
pClassInfo->pOIDsMustContain = pOIDsMustNew;
|
|
pClassInfo->nNumOfMustContain = nCountMust + nCountMustSup;
|
|
if ( pOIDsMust )
|
|
FreeADsMem( pOIDsMust );
|
|
|
|
pOIDsMustNew = NULL;
|
|
}
|
|
|
|
if ( nCountMaySup > 0 )
|
|
{
|
|
pOIDsMayNew = (int *) AllocADsMem(
|
|
sizeof(int) * ( nCountMay + nCountMaySup + 1 ));
|
|
|
|
if ( pOIDsMayNew == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
for ( i = 0; i < nCountMaySup; i++ )
|
|
{
|
|
pOIDsMayNew[i] = pOIDsMaySup[i];
|
|
}
|
|
|
|
for ( i = 0; i < nCountMay; i++ )
|
|
{
|
|
pOIDsMayNew[i+nCountMaySup] = pOIDsMay[i];
|
|
}
|
|
|
|
pOIDsMayNew[nCountMaySup+nCountMay] = -1;
|
|
|
|
pClassInfo->pOIDsMayContain = pOIDsMayNew;
|
|
pClassInfo->nNumOfMayContain = nCountMay + nCountMaySup;
|
|
if ( pOIDsMay )
|
|
FreeADsMem( pOIDsMay );
|
|
|
|
pOIDsMayNew = NULL;
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if ( pOIDsMustNew )
|
|
FreeADsMem( pOIDsMustNew );
|
|
|
|
if ( pOIDsMayNew )
|
|
FreeADsMem( pOIDsMayNew );
|
|
}
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
|
|
HRESULT ProcessClassInfoArray(
|
|
CLASSINFO *aClasses,
|
|
DWORD nClasses,
|
|
SEARCHENTRY *aSearchTable,
|
|
BOOL fProcessAUX // defaulted to false
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
SEARCHENTRY searchEntry;
|
|
SEARCHENTRY *matchedEntry;
|
|
DWORD i;
|
|
BOOL fProcessedAll = TRUE;
|
|
DWORD nLoop = 0;
|
|
|
|
do
|
|
{
|
|
fProcessedAll = TRUE;
|
|
|
|
for ( DWORD i = 0; i < nClasses; i++ )
|
|
{
|
|
LPTSTR *pOIDsSup = NULL;
|
|
LPTSTR *pOIDsAux = NULL;
|
|
DWORD j = 0;
|
|
|
|
if ( aClasses[i].fProcessedSuperiorClasses )
|
|
continue;
|
|
|
|
pOIDsSup = aClasses[i].pOIDsSuperiorClasses;
|
|
//
|
|
// If fProcessAUX then we ne need to add the aux
|
|
// class lists must and may to the classes own list.
|
|
// Please look at where this flag is being set for
|
|
// more details.
|
|
//
|
|
if (fProcessAUX) {
|
|
pOIDsAux = aClasses[i].pOIDsAuxClasses;
|
|
}
|
|
else {
|
|
pOIDsAux = NULL;
|
|
}
|
|
|
|
if ( pOIDsSup == NULL )
|
|
{
|
|
aClasses[i].fProcessedSuperiorClasses = TRUE;
|
|
continue;
|
|
}
|
|
|
|
// This is here just in case the schema description for class "top"
|
|
// has other superior classes. "top" should not have any superior
|
|
// classes.
|
|
|
|
if ( _tcsicmp( aClasses[i].pszName, TEXT("top")) == 0 )
|
|
{
|
|
aClasses[i].fProcessedSuperiorClasses = TRUE;
|
|
continue;
|
|
}
|
|
|
|
j = 0;
|
|
while ( pOIDsSup[j] )
|
|
{
|
|
searchEntry.pszName = pOIDsSup[j];
|
|
matchedEntry = (SEARCHENTRY *) bsearch(
|
|
(SEARCHENTRY *) &searchEntry,
|
|
aSearchTable, 2*nClasses,
|
|
sizeof(SEARCHENTRY), searchentrycmp );
|
|
|
|
if ( matchedEntry ) // found the superior class
|
|
{
|
|
if (!aClasses[matchedEntry->nIndex].fProcessedSuperiorClasses)
|
|
{
|
|
fProcessedAll = FALSE;
|
|
break;
|
|
}
|
|
|
|
hr = GetInfoFromSuperiorClasses(
|
|
&(aClasses[i]), &(aClasses[matchedEntry->nIndex]));
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
if ( pOIDsSup[j] == NULL )
|
|
{
|
|
if ( pOIDsAux == NULL )
|
|
{
|
|
aClasses[i].fProcessedSuperiorClasses = TRUE;
|
|
}
|
|
else
|
|
{
|
|
j = 0;
|
|
while ( pOIDsAux[j] )
|
|
{
|
|
searchEntry.pszName = pOIDsAux[j];
|
|
matchedEntry = (SEARCHENTRY *) bsearch(
|
|
(SEARCHENTRY *) &searchEntry,
|
|
aSearchTable, 2*nClasses,
|
|
sizeof(SEARCHENTRY), searchentrycmp);
|
|
|
|
if ( matchedEntry ) // found the superior class
|
|
{
|
|
if (!aClasses[matchedEntry->nIndex].fProcessedSuperiorClasses)
|
|
{
|
|
fProcessedAll = FALSE;
|
|
break;
|
|
}
|
|
|
|
hr = GetInfoFromSuperiorClasses(
|
|
&(aClasses[i]),
|
|
&(aClasses[matchedEntry->nIndex]));
|
|
BAIL_ON_FAILURE(hr);
|
|
}
|
|
|
|
j++;
|
|
}
|
|
|
|
if ( pOIDsAux[j] == NULL )
|
|
aClasses[i].fProcessedSuperiorClasses = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
nLoop++;
|
|
|
|
} while ( (nLoop < MAX_LOOP_COUNT) && !fProcessedAll );
|
|
|
|
for ( i = 0; i < nClasses; i++ )
|
|
{
|
|
CLASSINFO *pClass = &(aClasses[i]);
|
|
DWORD j, k;
|
|
|
|
//
|
|
// Eliminate duplicates in MustContain
|
|
//
|
|
|
|
if ( pClass->pOIDsMustContain != NULL )
|
|
{
|
|
SortAndRemoveDuplicateOIDs( pClass->pOIDsMustContain,
|
|
&pClass->nNumOfMustContain );
|
|
}
|
|
|
|
//
|
|
// Eliminate duplicates in MayContain
|
|
//
|
|
|
|
if ( pClass->pOIDsMayContain != NULL )
|
|
{
|
|
SortAndRemoveDuplicateOIDs( pClass->pOIDsMayContain,
|
|
&pClass->nNumOfMayContain );
|
|
}
|
|
|
|
//
|
|
// Eliminate duplicates between MustContain and MayContain
|
|
//
|
|
if ( ( pClass->pOIDsMustContain == NULL )
|
|
|| ( pClass->pOIDsMayContain == NULL )
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
j = 0; k = 0;
|
|
while ( ( pClass->pOIDsMustContain[j] != -1 )
|
|
&& ( pClass->pOIDsMayContain[k] != -1 )
|
|
)
|
|
{
|
|
int nMust = pClass->pOIDsMustContain[j];
|
|
int nMay = pClass->pOIDsMayContain[k];
|
|
|
|
if ( nMust < nMay )
|
|
{
|
|
j++;
|
|
}
|
|
else if ( nMust > nMay )
|
|
{
|
|
k++;
|
|
}
|
|
else // nMust == nMay
|
|
{
|
|
j++;
|
|
|
|
DWORD m = k;
|
|
|
|
do {
|
|
pClass->pOIDsMayContain[m] = pClass->pOIDsMayContain[m+1];
|
|
m++;
|
|
} while ( pClass->pOIDsMayContain[m] != -1 );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Removes the NotContain from the MustContain or MayContain list
|
|
//
|
|
|
|
if ( pClass->pOIDsNotContain != NULL )
|
|
{
|
|
qsort( pClass->pOIDsNotContain, pClass->nNumOfNotContain,
|
|
sizeof(pClass->pOIDsNotContain[0]), intcmp );
|
|
|
|
j = 0; k = 0;
|
|
while ( ( pClass->pOIDsMustContain[j] != -1 )
|
|
&& ( pClass->pOIDsNotContain[k] != -1 )
|
|
)
|
|
{
|
|
int nMust = pClass->pOIDsMustContain[j];
|
|
int nNot = pClass->pOIDsNotContain[k];
|
|
|
|
if ( nMust < nNot )
|
|
{
|
|
j++;
|
|
}
|
|
else if ( nMust > nNot )
|
|
{
|
|
k++;
|
|
}
|
|
else // nMust == nNot
|
|
{
|
|
k++;
|
|
|
|
DWORD m = j;
|
|
|
|
do {
|
|
pClass->pOIDsMustContain[m] = pClass->pOIDsMustContain[m+1];
|
|
m++;
|
|
} while ( pClass->pOIDsMustContain[m] != -1 );
|
|
}
|
|
}
|
|
|
|
j = 0; k = 0;
|
|
while ( ( pClass->pOIDsMayContain[j] != -1 )
|
|
&& ( pClass->pOIDsNotContain[k] != -1 )
|
|
)
|
|
{
|
|
int nMay = pClass->pOIDsMayContain[j];
|
|
int nNot = pClass->pOIDsNotContain[k];
|
|
|
|
if ( nMay < nNot )
|
|
{
|
|
j++;
|
|
}
|
|
else if ( nMay > nNot )
|
|
{
|
|
k++;
|
|
}
|
|
else // nMay == nNot
|
|
{
|
|
k++;
|
|
|
|
DWORD m = j;
|
|
|
|
do {
|
|
pClass->pOIDsMayContain[m] = pClass->pOIDsMayContain[m+1];
|
|
m++;
|
|
} while ( pClass->pOIDsMayContain[m] != -1 );
|
|
}
|
|
}
|
|
|
|
FreeADsMem( pClass->pOIDsNotContain );
|
|
pClass->pOIDsNotContain = NULL;
|
|
}
|
|
}
|
|
|
|
RRETURN(S_OK);
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
VOID SortAndRemoveDuplicateOIDs(
|
|
int *aOIDs,
|
|
DWORD *pnNumOfOIDs
|
|
)
|
|
{
|
|
DWORD j, k;
|
|
|
|
qsort( aOIDs, *pnNumOfOIDs, sizeof( aOIDs[0]), intcmp );
|
|
|
|
// The following code removes duplicate strings in place.
|
|
// index j is the index to walk the array, and index k is the
|
|
// index of the last non-duplicate entry. The array is sorted
|
|
// and so we compare the string at index j and index k.
|
|
// (1) If they are the same, then j++
|
|
// (2) If not the same, increment k, free the string at k and
|
|
// make k point to the string at j. Then increment j and
|
|
// continue.
|
|
//
|
|
// NOTE: aOIDs must be an array of integers that ends with -1.
|
|
|
|
|
|
j = 1; k = 0;
|
|
while ( aOIDs[j] != -1 )
|
|
{
|
|
while ( aOIDs[j] == aOIDs[k] )
|
|
{
|
|
j++;
|
|
if ( aOIDs[j] == -1 )
|
|
break;
|
|
}
|
|
|
|
if ( aOIDs[j] != -1 )
|
|
{
|
|
k++;
|
|
|
|
if ( k != j )
|
|
{
|
|
aOIDs[k] = aOIDs[j];
|
|
aOIDs[j] = -1;
|
|
}
|
|
j++;
|
|
}
|
|
}
|
|
|
|
k++;
|
|
aOIDs[k] = -1;
|
|
*pnNumOfOIDs = k;
|
|
}
|
|
|
|
VOID FreeClassInfoArray(
|
|
CLASSINFO *aClasses,
|
|
DWORD nClasses
|
|
)
|
|
{
|
|
if ( aClasses )
|
|
{
|
|
DWORD j;
|
|
for ( j = 0; j < nClasses; j++ )
|
|
{
|
|
FreeADsStr( aClasses[j].pszName );
|
|
FreeADsStr( aClasses[j].pszOID );
|
|
FreeADsStr( aClasses[j].pszHelpFileName );
|
|
FreeADsStr( aClasses[j].pszDescription );
|
|
|
|
DWORD k = 0;
|
|
if ( aClasses[j].pOIDsSuperiorClasses )
|
|
{
|
|
while ( aClasses[j].pOIDsSuperiorClasses[k] )
|
|
FreeADsStr( aClasses[j].pOIDsSuperiorClasses[k++]);
|
|
FreeADsMem( aClasses[j].pOIDsSuperiorClasses );
|
|
}
|
|
|
|
k = 0;
|
|
if ( aClasses[j].pOIDsAuxClasses )
|
|
{
|
|
while ( aClasses[j].pOIDsAuxClasses[k] )
|
|
FreeADsStr( aClasses[j].pOIDsAuxClasses[k++]);
|
|
FreeADsMem( aClasses[j].pOIDsAuxClasses );
|
|
}
|
|
|
|
if ( aClasses[j].pOIDsMustContain )
|
|
{
|
|
FreeADsMem( aClasses[j].pOIDsMustContain );
|
|
}
|
|
|
|
if ( aClasses[j].pOIDsMayContain )
|
|
{
|
|
FreeADsMem( aClasses[j].pOIDsMayContain );
|
|
}
|
|
|
|
if ( aClasses[j].pOIDsNotContain )
|
|
{
|
|
FreeADsMem( aClasses[j].pOIDsNotContain );
|
|
}
|
|
}
|
|
|
|
FreeADsMem( aClasses );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD ReadSchemaInfoFromFileWithHandle(
|
|
HANDLE hFile,
|
|
LPTSTR **paValuesAttrTypes,
|
|
int *pnCountAttrTypes,
|
|
LPTSTR **paValuesObjClasses,
|
|
int *pnCountObjClasses,
|
|
LPTSTR **paValuesRules,
|
|
int *pnCountRules,
|
|
LPBYTE *pFileBuffer
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
DWORD dwFileSize = 0;
|
|
DWORD dwBytesRead = 0;
|
|
LPTSTR pLine = NULL;
|
|
LPTSTR pEndOfLine = NULL;
|
|
DWORD nCount;
|
|
DWORD nType;
|
|
DWORD dwStatus = NO_ERROR;
|
|
//
|
|
// Even though the calling routine has an exception handler,
|
|
// we need one over here also as this we need to close the file
|
|
// over here, we do not have the file handle in the calling routine.
|
|
//
|
|
__try {
|
|
|
|
dwFileSize = GetFileSize( hFile, NULL );
|
|
|
|
if ( dwFileSize == -1 )
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
else if ( dwFileSize == 0 )
|
|
{
|
|
err = ERROR_FILE_INVALID;
|
|
goto cleanup;
|
|
}
|
|
|
|
*pFileBuffer = (LPBYTE) AllocADsMem( dwFileSize );
|
|
if ( *pFileBuffer == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !ReadFile( hFile,
|
|
*pFileBuffer,
|
|
dwFileSize,
|
|
&dwBytesRead,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
for ( pLine = ((LPTSTR) *pFileBuffer) + 1; // go past Unicode BOM marker
|
|
pLine < (LPTSTR) ( *pFileBuffer + dwFileSize );
|
|
pLine = pEndOfLine + 1 )
|
|
{
|
|
if ( pEndOfLine = _tcschr( pLine, TEXT('\n')))
|
|
*pEndOfLine = 0;
|
|
|
|
if ( _tcsicmp( pLine, TEXT("[attributeTypes]")) == 0 )
|
|
{
|
|
nType = ID_ATTRTYPES;
|
|
continue;
|
|
}
|
|
else if ( _tcsicmp( pLine, TEXT("[objectClasses]")) == 0 )
|
|
{
|
|
nType = ID_OBJCLASSES;
|
|
continue;
|
|
}
|
|
else if ( _tcsicmp( pLine, TEXT("[DITContentRules]")) == 0 )
|
|
{
|
|
nType = ID_DITCONTENTRULES;
|
|
continue;
|
|
}
|
|
|
|
switch ( nType )
|
|
{
|
|
case ID_ATTRTYPES:
|
|
(*pnCountAttrTypes)++;
|
|
break;
|
|
|
|
case ID_OBJCLASSES:
|
|
(*pnCountObjClasses)++;
|
|
break;
|
|
|
|
case ID_DITCONTENTRULES:
|
|
(*pnCountRules)++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
*paValuesAttrTypes = (LPTSTR *) AllocADsMem( *pnCountAttrTypes * sizeof(LPTSTR));
|
|
if ( *paValuesAttrTypes == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
*paValuesObjClasses = (LPTSTR *) AllocADsMem( *pnCountObjClasses * sizeof(LPTSTR));
|
|
if ( *paValuesObjClasses == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
*paValuesRules = (LPTSTR *) AllocADsMem( *pnCountRules * sizeof(LPTSTR));
|
|
if ( *paValuesRules == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
for ( pLine = ((LPTSTR) *pFileBuffer) + 1;// go past Unicode BOM marker
|
|
pLine < (LPTSTR) ( *pFileBuffer + dwFileSize );
|
|
pLine += ( _tcslen(pLine) + 1) )
|
|
{
|
|
if ( _tcsicmp( pLine, TEXT("[attributeTypes]")) == 0 )
|
|
{
|
|
nCount = 0;
|
|
nType = ID_ATTRTYPES;
|
|
continue;
|
|
}
|
|
else if ( _tcsicmp( pLine, TEXT("[objectClasses]")) == 0 )
|
|
{
|
|
nCount = 0;
|
|
nType = ID_OBJCLASSES;
|
|
continue;
|
|
}
|
|
else if ( _tcsicmp( pLine, TEXT("[DITContentRules]")) == 0 )
|
|
{
|
|
nCount = 0;
|
|
nType = ID_DITCONTENTRULES;
|
|
continue;
|
|
}
|
|
|
|
switch ( nType )
|
|
{
|
|
case ID_ATTRTYPES:
|
|
(*paValuesAttrTypes)[nCount++] = pLine;
|
|
break;
|
|
|
|
case ID_OBJCLASSES:
|
|
(*paValuesObjClasses)[nCount++] = pLine;
|
|
break;
|
|
|
|
case ID_DITCONTENTRULES:
|
|
(*paValuesRules)[nCount++] = pLine;
|
|
break;
|
|
}
|
|
}
|
|
} __except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
err = GetExceptionCode();
|
|
|
|
if (dwStatus != EXCEPTION_ACCESS_VIOLATION) {
|
|
ADsDebugOut((DEB_ERROR, "Processing Schema Info:Unknown Exception %d\n", err));
|
|
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
CloseHandle( hFile );
|
|
|
|
if ( err )
|
|
{
|
|
*pnCountAttrTypes = 0;
|
|
*pnCountObjClasses = 0;
|
|
*pnCountRules = 0;
|
|
|
|
if ( *paValuesAttrTypes )
|
|
FreeADsMem( *paValuesAttrTypes );
|
|
|
|
if ( *paValuesObjClasses )
|
|
FreeADsMem( *paValuesObjClasses );
|
|
|
|
if ( *paValuesRules )
|
|
FreeADsMem( *paValuesRules );
|
|
|
|
if ( *pFileBuffer )
|
|
FreeADsMem( *pFileBuffer );
|
|
|
|
*paValuesAttrTypes = NULL;
|
|
*paValuesObjClasses = NULL;
|
|
*paValuesRules = NULL;
|
|
*pFileBuffer = NULL;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
#ifdef WIN95
|
|
DWORD ReadSchemaInfoFromFileA(
|
|
LPSTR pszFile,
|
|
LPTSTR **paValuesAttrTypes,
|
|
int *pnCountAttrTypes,
|
|
LPTSTR **paValuesObjClasses,
|
|
int *pnCountObjClasses,
|
|
LPTSTR **paValuesRules,
|
|
int *pnCountRules,
|
|
LPBYTE *pFileBuffer
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
HANDLE hFile = NULL;
|
|
|
|
|
|
*pnCountAttrTypes = 0;
|
|
*pnCountObjClasses = 0;
|
|
*pnCountRules = 0;
|
|
|
|
|
|
hFile = CreateFileA( pszFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
return GetLastError();
|
|
|
|
return (
|
|
ReadSchemaInfoFromFileWithHandle(
|
|
hFile,
|
|
paValuesAttrTypes,
|
|
pnCountAttrTypes,
|
|
paValuesObjClasses,
|
|
pnCountObjClasses,
|
|
paValuesRules,
|
|
pnCountRules,
|
|
pFileBuffer
|
|
)
|
|
);
|
|
}
|
|
#endif
|
|
|
|
DWORD ReadSchemaInfoFromFileW(
|
|
LPTSTR pszFile,
|
|
LPTSTR **paValuesAttrTypes,
|
|
int *pnCountAttrTypes,
|
|
LPTSTR **paValuesObjClasses,
|
|
int *pnCountObjClasses,
|
|
LPTSTR **paValuesRules,
|
|
int *pnCountRules,
|
|
LPBYTE *pFileBuffer
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
HANDLE hFile = NULL;
|
|
|
|
*pnCountAttrTypes = 0;
|
|
*pnCountObjClasses = 0;
|
|
*pnCountRules = 0;
|
|
|
|
|
|
hFile = CreateFile( pszFile,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
return GetLastError();
|
|
|
|
return (
|
|
ReadSchemaInfoFromFileWithHandle(
|
|
hFile,
|
|
paValuesAttrTypes,
|
|
pnCountAttrTypes,
|
|
paValuesObjClasses,
|
|
pnCountObjClasses,
|
|
paValuesRules,
|
|
pnCountRules,
|
|
pFileBuffer
|
|
)
|
|
);
|
|
|
|
}
|
|
|
|
|
|
DWORD StoreSchemaInfoToFileWithHandle(
|
|
HANDLE hFile,
|
|
LPTSTR *aValuesAttribTypes,
|
|
int nCountAttribTypes,
|
|
LPTSTR *aValuesObjClasses,
|
|
int nCountObjClasses,
|
|
LPTSTR *aValuesRules,
|
|
int nCountRules
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
TCHAR szEndOfLine[2] = TEXT("\n");
|
|
TCHAR szSection[MAX_PATH];
|
|
DWORD dwBytesWritten;
|
|
int i;
|
|
|
|
szSection[0] = 0xFEFF; // Unicode BOM marker
|
|
if ( !WriteFile( hFile,
|
|
szSection,
|
|
sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
_tcscpy( szSection, TEXT("[attributeTypes]\n"));
|
|
if ( !WriteFile( hFile,
|
|
szSection,
|
|
_tcslen( szSection ) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
for ( i = 0; i < nCountAttribTypes; i++ )
|
|
{
|
|
if ( !WriteFile( hFile,
|
|
aValuesAttribTypes[i],
|
|
_tcslen( aValuesAttribTypes[i] ) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !WriteFile( hFile,
|
|
szEndOfLine,
|
|
sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
_tcscpy( szSection, TEXT("[objectClasses]\n"));
|
|
if ( !WriteFile( hFile,
|
|
szSection,
|
|
_tcslen( szSection ) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
for ( i = 0; i < nCountObjClasses; i++ )
|
|
{
|
|
if ( !WriteFile( hFile,
|
|
aValuesObjClasses[i],
|
|
_tcslen( aValuesObjClasses[i] ) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !WriteFile( hFile,
|
|
szEndOfLine,
|
|
sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
_tcscpy( szSection, TEXT("[DITContentRules]\n"));
|
|
if ( !WriteFile( hFile,
|
|
szSection,
|
|
_tcslen( szSection ) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
for ( i = 0; i < nCountRules; i++ )
|
|
{
|
|
if ( !WriteFile( hFile,
|
|
aValuesRules[i],
|
|
_tcslen( aValuesRules[i] ) * sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( !WriteFile( hFile,
|
|
szEndOfLine,
|
|
sizeof(TCHAR),
|
|
&dwBytesWritten,
|
|
NULL ))
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
|
|
CloseHandle( hFile );
|
|
|
|
return err;
|
|
}
|
|
|
|
#ifdef WIN95
|
|
DWORD StoreSchemaInfoToFileA(
|
|
LPSTR pszFile,
|
|
LPTSTR *aValuesAttribTypes,
|
|
int nCountAttribTypes,
|
|
LPTSTR *aValuesObjClasses,
|
|
int nCountObjClasses,
|
|
LPTSTR *aValuesRules,
|
|
int nCountRules
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
HANDLE hFile = NULL;
|
|
|
|
|
|
hFile = CreateFileA( pszFile,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
err = GetLastError();
|
|
|
|
if ( err == ERROR_PATH_NOT_FOUND )
|
|
{
|
|
//
|
|
// The directory is not created yet, create it now.
|
|
//
|
|
|
|
LPSTR pszTemp = strstr( pszFile, "SchCache\\");
|
|
pszTemp += strlen("SchCache");
|
|
*pszTemp = 0;
|
|
|
|
if ( !CreateDirectoryA( pszFile, NULL ))
|
|
return GetLastError();
|
|
|
|
*pszTemp = '\\';
|
|
hFile = CreateFileA( pszFile,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
return GetLastError();
|
|
|
|
err = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
return err;
|
|
}
|
|
}
|
|
|
|
err = StoreSchemaInfoToFileWithHandle(
|
|
hFile,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules
|
|
);
|
|
|
|
if ( err != NO_ERROR )
|
|
DeleteFileA( pszFile );
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
DWORD StoreSchemaInfoToFileW(
|
|
LPTSTR pszFile,
|
|
LPTSTR *aValuesAttribTypes,
|
|
int nCountAttribTypes,
|
|
LPTSTR *aValuesObjClasses,
|
|
int nCountObjClasses,
|
|
LPTSTR *aValuesRules,
|
|
int nCountRules
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
HANDLE hFile = NULL;
|
|
|
|
|
|
hFile = CreateFile( pszFile,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
{
|
|
err = GetLastError();
|
|
|
|
if ( err == ERROR_PATH_NOT_FOUND )
|
|
{
|
|
// The directory is not created yet, create it now.
|
|
|
|
LPTSTR pszTemp = _tcsrchr( pszFile, TEXT('\\'));
|
|
*pszTemp = 0;
|
|
|
|
if ( !CreateDirectory( pszFile, NULL ))
|
|
return GetLastError();
|
|
|
|
*pszTemp = TEXT('\\');
|
|
hFile = CreateFile( pszFile,
|
|
GENERIC_WRITE,
|
|
0,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if ( hFile == INVALID_HANDLE_VALUE )
|
|
return GetLastError();
|
|
|
|
err = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
return err;
|
|
}
|
|
}
|
|
|
|
err = StoreSchemaInfoToFileWithHandle(
|
|
hFile,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules
|
|
);
|
|
|
|
if ( err != NO_ERROR )
|
|
DeleteFile( pszFile );
|
|
|
|
return err;
|
|
}
|
|
|
|
DWORD ReadSchemaInfoFromRegistry(
|
|
HKEY hKey,
|
|
LPWSTR pszServer,
|
|
LPTSTR **paValuesAttribTypes,
|
|
int *pnCountAttribTypes,
|
|
LPTSTR **paValuesObjClasses,
|
|
int *pnCountObjClasses,
|
|
LPTSTR **paValuesRules,
|
|
int *pnCountRules,
|
|
LPBYTE *pBuffer
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
LPTSTR pszPath = NULL;
|
|
LPTSTR pszExpandedPath = NULL;
|
|
LPSTR pszPathAscii = NULL;
|
|
LPSTR pszExpandedPathAscii = NULL;
|
|
LPTSTR pszTempPath = NULL;
|
|
DWORD dwLength = 0;
|
|
DWORD dwType;
|
|
|
|
|
|
//
|
|
// On chk bits would be good to make sure that this never
|
|
// happens as some schema stuff is messed up in this case.
|
|
//
|
|
if (!pszServer) {
|
|
ADsAssert(!"No server name was passed");
|
|
}
|
|
//
|
|
// Get the file name that is used to store the schema info
|
|
// from the registry.
|
|
//
|
|
|
|
err = RegQueryValueEx( hKey,
|
|
SCHEMA_FILE_NAME,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwLength );
|
|
#ifndef WIN95
|
|
if ( err )
|
|
goto cleanup;
|
|
#else
|
|
if (err == ERROR_MORE_DATA ) {
|
|
//
|
|
// Continue cause Win9x is dumb.
|
|
//
|
|
err = 0;
|
|
} else
|
|
goto cleanup;
|
|
#endif
|
|
|
|
pszPath = (LPTSTR) AllocADsMem( dwLength );
|
|
if ( pszPath == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
err = RegQueryValueEx( hKey,
|
|
SCHEMA_FILE_NAME,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) pszPath,
|
|
&dwLength );
|
|
|
|
if ( err )
|
|
goto cleanup;
|
|
|
|
//
|
|
// Expand the path
|
|
//
|
|
pszExpandedPath = (LPTSTR) AllocADsMem( MAX_PATH * sizeof(WCHAR));
|
|
if ( pszExpandedPath == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// At this point we want to rename all files called default.sch
|
|
// We look for default.sch in the string and then create a new
|
|
// string. For example if the string is %systemroot%\SCHEMA_DIR\
|
|
// Default.sch we will replace with %systemroot%\SCHEMA_DIR\
|
|
// pszServer.sch. This change will force schema to be dropped
|
|
// and we will end up picking up and storing the schema under the
|
|
// correct name. This will ensure that 2 different forests do
|
|
// not end up creating conflicting default.sch files that we
|
|
// never recover from internally. This excercise is futile unless
|
|
// a server name was passed in (should be the case always).
|
|
//
|
|
if (pszPath && *pszPath && pszServer) {
|
|
//
|
|
// Look for default.sch
|
|
//
|
|
pszTempPath = wcsstr( pszPath, DEFAULT_SCHEMA_FILE_NAME_WITH_EXT);
|
|
|
|
if (pszTempPath) {
|
|
//
|
|
// We now need to replace this string.
|
|
//
|
|
DWORD dwLenStr, dwLenNewPath;
|
|
dwLenStr = pszTempPath - pszPath;
|
|
|
|
//
|
|
// Now build the new string from the old one. Length is
|
|
// pszServer + the old piece upto schema file name.
|
|
//
|
|
dwLenNewPath = wcslen(pszServer)
|
|
+ wcslen(SCHEMA_FILE_NAME_EXT)
|
|
+ dwLenStr
|
|
+ 1;
|
|
|
|
pszTempPath = (LPTSTR) AllocADsMem(dwLenNewPath * sizeof(WCHAR));
|
|
if (!pszTempPath) {
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
wcsncpy(pszTempPath, pszPath, dwLenStr);
|
|
wcscat(pszTempPath, pszServer);
|
|
wcscat(pszTempPath, SCHEMA_FILE_NAME_EXT);
|
|
|
|
FreeADsMem(pszPath);
|
|
pszPath = pszTempPath;
|
|
pszTempPath = NULL;
|
|
|
|
//
|
|
// Now update the key in the registry. Ignore the error
|
|
// cause there is nothing we can really do about it.
|
|
//
|
|
err = RegSetValueEx(
|
|
hKey,
|
|
SCHEMA_FILE_NAME,
|
|
0,
|
|
REG_EXPAND_SZ,
|
|
(CONST BYTE *) pszPath,
|
|
(_tcslen(pszPath) + 1 ) * sizeof(TCHAR)
|
|
);
|
|
}
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStrings( pszPath,
|
|
pszExpandedPath,
|
|
MAX_PATH );
|
|
|
|
#ifdef WIN95
|
|
//
|
|
// Just in case 3 bytes for each WCHAR rather than just 2.
|
|
//
|
|
pszExpandedPathAscii = (LPSTR) AllocADsMem( MAX_PATH * sizeof(char) * 3);
|
|
if (!pszExpandedPathAscii) {
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (err = ConvertToAscii(pszPath, &pszPathAscii)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStringsA(
|
|
pszPathAscii,
|
|
pszExpandedPathAscii,
|
|
MAX_PATH * sizeof(char) * 3
|
|
);
|
|
#endif
|
|
|
|
if ( dwLength == 0 )
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
else if ( dwLength > MAX_PATH )
|
|
{
|
|
FreeADsMem( pszExpandedPath );
|
|
pszExpandedPath = (LPTSTR) AllocADsMem( dwLength * sizeof(WCHAR));
|
|
if ( pszExpandedPath == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStrings( pszPath,
|
|
pszExpandedPath,
|
|
dwLength );
|
|
if ( dwLength == 0 )
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now, read the info from the file
|
|
//
|
|
#ifndef WIN95
|
|
err = ReadSchemaInfoFromFileW(
|
|
pszExpandedPath,
|
|
paValuesAttribTypes,
|
|
pnCountAttribTypes,
|
|
paValuesObjClasses,
|
|
pnCountObjClasses,
|
|
paValuesRules,
|
|
pnCountRules,
|
|
pBuffer
|
|
);
|
|
#else
|
|
err = ReadSchemaInfoFromFileA(
|
|
pszExpandedPathAscii,
|
|
paValuesAttribTypes,
|
|
pnCountAttribTypes,
|
|
paValuesObjClasses,
|
|
pnCountObjClasses,
|
|
paValuesRules,
|
|
pnCountRules,
|
|
pBuffer
|
|
);
|
|
|
|
#endif
|
|
|
|
|
|
cleanup:
|
|
|
|
if ( pszPath )
|
|
FreeADsMem( pszPath );
|
|
|
|
if ( pszExpandedPath )
|
|
FreeADsMem( pszExpandedPath );
|
|
|
|
if (pszTempPath) {
|
|
FreeADsMem(pszTempPath);
|
|
}
|
|
|
|
#ifdef WIN95
|
|
if (pszPathAscii) {
|
|
FreeADsMem(pszPathAscii);
|
|
}
|
|
|
|
if (pszExpandedPathAscii) {
|
|
FreeADsMem(pszExpandedPathAscii);
|
|
}
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
|
|
DWORD StoreSchemaInfoInRegistry(
|
|
HKEY hKey,
|
|
LPTSTR pszServer,
|
|
LPTSTR pszTime,
|
|
LPTSTR *aValuesAttribTypes,
|
|
int nCountAttribTypes,
|
|
LPTSTR *aValuesObjClasses,
|
|
int nCountObjClasses,
|
|
LPTSTR *aValuesRules,
|
|
int nCountRules,
|
|
BOOL fProcessAUX
|
|
)
|
|
{
|
|
DWORD err = NO_ERROR;
|
|
LPTSTR pszPath = NULL;
|
|
LPTSTR pszExpandedPath = NULL;
|
|
LPSTR pszPathAscii = NULL;
|
|
LPSTR pszExpandedPathAscii = NULL;
|
|
DWORD dwLength = 0;
|
|
DWORD dwType;
|
|
DWORD dwProcessAUX;
|
|
|
|
//
|
|
// See if we can find the file name that is used to store the schema info
|
|
// in the registry.
|
|
//
|
|
|
|
err = RegQueryValueEx( hKey,
|
|
SCHEMA_FILE_NAME,
|
|
NULL,
|
|
&dwType,
|
|
NULL,
|
|
&dwLength );
|
|
|
|
if ( err == NO_ERROR )
|
|
{
|
|
pszPath = (LPTSTR) AllocADsMem( dwLength );
|
|
if ( pszPath == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
err = RegQueryValueEx( hKey,
|
|
SCHEMA_FILE_NAME,
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE) pszPath,
|
|
&dwLength );
|
|
|
|
if ( err )
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
err = NO_ERROR;
|
|
|
|
if ( pszPath == NULL ) // Cannot read from the registry
|
|
{
|
|
//
|
|
// Allocate pszPath to be either MAX_PATH chars or sufficient
|
|
// to hold %SystemRoot%/<SCHEMA_DIR_NAME>/<pszServer>.sch, whichever
|
|
// is larger.
|
|
//
|
|
if (pszServer) {
|
|
DWORD cbPath = (MAX_PATH > (_tcslen(TEXT("%SystemRoot%\\")) +
|
|
_tcslen(SCHEMA_DIR_NAME) +
|
|
_tcslen(pszServer) +
|
|
_tcslen(SCHEMA_FILE_NAME_EXT))) ?
|
|
(MAX_PATH * sizeof(WCHAR)) :
|
|
(2 * _tcslen(pszServer) * sizeof(WCHAR));
|
|
|
|
|
|
pszPath = (LPTSTR) AllocADsMem(cbPath);
|
|
}
|
|
else{
|
|
//
|
|
// pszServe can be NULL in the ldapc layer users case
|
|
//
|
|
pszPath = (LPTSTR) AllocADsMem(MAX_PATH * sizeof(WCHAR));
|
|
}
|
|
|
|
if ( pszPath == NULL )
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
//
|
|
// Build the path of the schema info file
|
|
//
|
|
#ifndef WIN95
|
|
_tcscpy( pszPath, TEXT("%SystemRoot%\\"));
|
|
#else
|
|
_tcscpy( pszPath, TEXT("%WINDIR%\\"));
|
|
#endif
|
|
_tcscat( pszPath, SCHEMA_DIR_NAME);
|
|
if (pszServer) {
|
|
|
|
//
|
|
// Server strings may have a port number in them,
|
|
// e.g., "ntdev:389". We need to change this to
|
|
// "ntdev_389", otherwise the colon will give us trouble
|
|
// in the filename.
|
|
//
|
|
LPTSTR pszColon = _tcschr(pszServer, (TCHAR)':');
|
|
|
|
if (!pszColon) {
|
|
_tcscat( pszPath, pszServer);
|
|
}
|
|
else {
|
|
_tcsncat( pszPath, pszServer, pszColon-pszServer);
|
|
_tcscat ( pszPath, TEXT("_"));
|
|
_tcscat ( pszPath, pszColon+1); // the number after the colon
|
|
}
|
|
|
|
}
|
|
else {
|
|
_tcscat( pszPath, DEFAULT_SCHEMA_FILE_NAME);
|
|
}
|
|
|
|
_tcscat( pszPath, SCHEMA_FILE_NAME_EXT );
|
|
}
|
|
|
|
pszExpandedPath = (LPTSTR) AllocADsMem( MAX_PATH * sizeof(WCHAR) );
|
|
if ( pszExpandedPath == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStrings( pszPath,
|
|
pszExpandedPath,
|
|
MAX_PATH );
|
|
#ifdef WIN95
|
|
|
|
pszExpandedPathAscii = (LPSTR) AllocADsMem(MAX_PATH * sizeof(char) * 3);
|
|
if (!pszExpandedPathAscii) {
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (err = ConvertToAscii(pszPath, &pszPathAscii)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStringsA(
|
|
pszPathAscii,
|
|
pszExpandedPathAscii,
|
|
MAX_PATH * sizeof(char) * 3
|
|
);
|
|
#endif
|
|
|
|
if ( dwLength == 0 )
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
else if ( dwLength > MAX_PATH )
|
|
{
|
|
FreeADsMem( pszExpandedPath );
|
|
pszExpandedPath = (LPTSTR) AllocADsMem( dwLength * sizeof(WCHAR) );
|
|
if ( pszExpandedPath == NULL )
|
|
{
|
|
err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto cleanup;
|
|
}
|
|
|
|
dwLength = ExpandEnvironmentStrings( pszPath,
|
|
pszExpandedPath,
|
|
dwLength );
|
|
if ( dwLength == 0 )
|
|
{
|
|
err = GetLastError();
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Write the schema information into the file
|
|
//
|
|
#ifndef WIN95
|
|
err = StoreSchemaInfoToFileW(
|
|
pszExpandedPath,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules
|
|
);
|
|
#else
|
|
err = StoreSchemaInfoToFileA(
|
|
pszExpandedPathAscii,
|
|
aValuesAttribTypes,
|
|
nCountAttribTypes,
|
|
aValuesObjClasses,
|
|
nCountObjClasses,
|
|
aValuesRules,
|
|
nCountRules
|
|
);
|
|
#endif
|
|
|
|
if ( err )
|
|
goto cleanup;
|
|
|
|
//
|
|
// Write the information into the registry
|
|
//
|
|
|
|
err = RegSetValueEx( hKey,
|
|
SCHEMA_FILE_NAME,
|
|
0,
|
|
REG_EXPAND_SZ,
|
|
(CONST BYTE *) pszPath,
|
|
(_tcslen(pszPath) + 1 ) * sizeof(TCHAR));
|
|
|
|
if ( err )
|
|
goto cleanup;
|
|
|
|
err = RegSetValueEx( hKey,
|
|
SCHEMA_TIME,
|
|
0,
|
|
REG_SZ,
|
|
(CONST BYTE *) pszTime,
|
|
(_tcslen(pszTime) + 1 ) * sizeof(TCHAR));
|
|
|
|
if ( err )
|
|
goto cleanup;
|
|
|
|
|
|
dwProcessAUX = (TRUE == fProcessAUX) ? 1: 0;
|
|
err = RegSetValueExW( hKey,
|
|
SCHEMA_PROCESSAUX,
|
|
0,
|
|
REG_DWORD,
|
|
(CONST BYTE *) &dwProcessAUX,
|
|
sizeof(dwProcessAUX));
|
|
|
|
|
|
cleanup:
|
|
|
|
if ( pszPath )
|
|
FreeADsMem( pszPath );
|
|
|
|
if ( pszExpandedPath )
|
|
FreeADsMem( pszExpandedPath );
|
|
|
|
#ifdef WIN95
|
|
if (pszPathAscii) {
|
|
FreeADsMem(pszPathAscii);
|
|
}
|
|
|
|
if (pszExpandedPathAscii) {
|
|
FreeADsMem(pszExpandedPathAscii);
|
|
}
|
|
#endif
|
|
|
|
return err;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: AttributeTypeDescription
|
|
//
|
|
// Synopsis: Parses an attribute type description.
|
|
// It parses the following grammar rules
|
|
//
|
|
// <AttributeTypeDescription> ::= "("
|
|
// <oid> -- AttributeType identifier
|
|
// [ "NAME" <DirectoryStrings> ] -- name used in AttributeType
|
|
// [ "DESC" <DirectoryString> ]
|
|
// [ "OBSOLETE" ]
|
|
// [ "SUP" <oid> ] -- derived from this other AttributeType
|
|
// [ "EQUALITY" <oid> ] -- Matching Rule name
|
|
// [ "ORDERING" <oid> ] -- Matching Rule name
|
|
// [ "SUBSTR" <oid> ] -- Matching Rule name
|
|
// [ "SYNTAX" <DirectoryString> ] -- see section 4.2
|
|
// [ "SINGLE-VALUE" ] -- default multi-valued
|
|
// [ "COLLECTIVE" ] -- default not collective
|
|
// [ "DYNAMIC" ] -- default not dynamic
|
|
// [ "NO-USER-MODIFICATION" ] -- default user modifiable
|
|
// [ "USAGE" <AttributeUsage> ] -- default user applications
|
|
// ")"
|
|
//
|
|
// <AttributeUsage> ::=
|
|
// "userApplications"
|
|
// | "directoryOperation"
|
|
// | "distributedOperation" -- DSA-shared
|
|
// | "dSAOperation" -- DSA-specific, value depends on server
|
|
//
|
|
//
|
|
// Arguments: [LPTSTR] pszAttrType : The string to parse
|
|
//
|
|
// Returns: [HRESULT] 0 if successful, error HRESULT if not
|
|
//
|
|
// Modifies: pTokenizer (consumes the input buffer)
|
|
//
|
|
// History: 9-3-96 yihsins Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
AttributeTypeDescription(
|
|
LPTSTR pszAttrType,
|
|
PPROPERTYINFO pPropertyInfo,
|
|
LPWSTR **pppszNames,
|
|
PDWORD pdwNameCount
|
|
)
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
|
|
CSchemaLexer Tokenizer( pszAttrType );
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken != TOKEN_OPENBRACKET )
|
|
RRETURN(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
|
|
//
|
|
// use TRUE flag as there is a chance that from
|
|
// some schemas, we get bad data that has no GUID
|
|
//
|
|
hr = Oid( &Tokenizer, &(pPropertyInfo->pszOID), TRUE);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
while ( TRUE ) {
|
|
LPWSTR *ppszDirStrings;
|
|
DWORD dwCount,dwCtr;
|
|
ppszDirStrings = NULL;
|
|
dwCount = 0;
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
Tokenizer.IsKeyword( szToken, &dwToken );
|
|
|
|
switch ( dwToken ) {
|
|
case TOKEN_CLOSEBRACKET:
|
|
RRETURN(S_OK);
|
|
|
|
case TOKEN_NAME:
|
|
hr = DirectoryStrings(
|
|
&Tokenizer,
|
|
&ppszDirStrings,
|
|
&dwCount
|
|
);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (!ppszDirStrings) {
|
|
//
|
|
// We need at least one name.
|
|
//
|
|
BAIL_IF_ERROR(
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
|
|
);
|
|
}
|
|
|
|
//
|
|
// For now we will only support the first name in the list.
|
|
//
|
|
pPropertyInfo->pszPropertyName = ppszDirStrings[0];
|
|
|
|
//
|
|
// The remaining values if any will require additional
|
|
// processing in FillPropertyInfoArray.
|
|
//
|
|
*pppszNames = ppszDirStrings;
|
|
*pdwNameCount = dwCount;
|
|
|
|
break;
|
|
|
|
case TOKEN_DESC:
|
|
hr = DirectoryString( &Tokenizer,
|
|
&(pPropertyInfo->pszDescription));
|
|
break;
|
|
|
|
case TOKEN_OBSOLETE:
|
|
// attribute is obsolete (RFC 2252)
|
|
pPropertyInfo->fObsolete = TRUE;
|
|
break;
|
|
|
|
case TOKEN_SUP:
|
|
hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDSup));
|
|
break;
|
|
|
|
case TOKEN_EQUALITY:
|
|
hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDEquality));
|
|
break;
|
|
|
|
case TOKEN_ORDERING:
|
|
hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDOrdering));
|
|
break;
|
|
|
|
case TOKEN_SUBSTR:
|
|
hr = Oid( &Tokenizer, &(pPropertyInfo->pszOIDSubstr));
|
|
break;
|
|
|
|
case TOKEN_SYNTAX:
|
|
hr = DirectoryString( &Tokenizer, &(pPropertyInfo->pszSyntax));
|
|
//
|
|
// It need not necessarily be a DirectoryString can also be
|
|
// an OID. So if DirectoryString fails, we should try OID.
|
|
//
|
|
if (FAILED(hr)
|
|
&& (hr == HRESULT_FROM_WIN32(ERROR_INVALID_DATA))
|
|
) {
|
|
|
|
Tokenizer.PushBackToken();
|
|
hr = Oid( &Tokenizer, &(pPropertyInfo->pszSyntax));
|
|
}
|
|
|
|
break;
|
|
|
|
case TOKEN_SINGLE_VALUE:
|
|
pPropertyInfo->fSingleValued = TRUE;
|
|
break;
|
|
|
|
case TOKEN_COLLECTIVE:
|
|
pPropertyInfo->fCollective = TRUE;
|
|
break;
|
|
|
|
case TOKEN_DYNAMIC:
|
|
pPropertyInfo->fDynamic = TRUE;
|
|
break;
|
|
|
|
case TOKEN_NO_USER_MODIFICATION:
|
|
pPropertyInfo->fNoUserModification = TRUE;
|
|
break;
|
|
|
|
case TOKEN_USAGE:
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (_tcsicmp(szToken, TEXT("userApplications")) == 0)
|
|
pPropertyInfo->dwUsage = ATTR_USAGE_USERAPPLICATIONS;
|
|
else if (_tcsicmp(szToken, TEXT("directoryOperation")) == 0)
|
|
pPropertyInfo->dwUsage = ATTR_USAGE_DIRECTORYOPERATION;
|
|
else if (_tcsicmp(szToken, TEXT("distributedOperation")) == 0)
|
|
pPropertyInfo->dwUsage = ATTR_USAGE_DISTRIBUTEDOPERATION;
|
|
else if (_tcsicmp(szToken, TEXT("dSAOperation")) == 0)
|
|
pPropertyInfo->dwUsage = ATTR_USAGE_DSAOPERATION;
|
|
break;
|
|
|
|
case TOKEN_OPEN_CURLY :
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (dwToken != TOKEN_IDENTIFIER) {
|
|
BAIL_IF_ERROR(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
}
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (dwToken != TOKEN_CLOSE_CURLY) {
|
|
BAIL_IF_ERROR(hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
}
|
|
break;
|
|
|
|
case TOKEN_X :
|
|
//
|
|
// This means that this token and the following
|
|
// DirectoryStrings token (which can be empty string)
|
|
// need to be ignored.
|
|
//
|
|
hr = DirectoryStrings(
|
|
&Tokenizer,
|
|
&ppszDirStrings,
|
|
&dwCount
|
|
);
|
|
//
|
|
// If we could not process this then we need to BAIL
|
|
// as the Tokenizer is not in a recoverable state.
|
|
//
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
//
|
|
// Free the strings that came back.
|
|
//
|
|
FreeDirectoryStrings(
|
|
ppszDirStrings,
|
|
dwCount
|
|
);
|
|
|
|
ppszDirStrings = NULL;
|
|
|
|
break;
|
|
|
|
default:
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
break;
|
|
}
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: ObjectClassDescription
|
|
//
|
|
// Synopsis: Parses an object class description.
|
|
// It parses the following grammar rules
|
|
//
|
|
// <ObjectClassDescription> ::= "("
|
|
// <oid> -- ObjectClass identifier
|
|
// [ "NAME" <DirectoryStrings> ]
|
|
// [ "DESC" <DirectoryString> ]
|
|
// [ "OBSOLETE" ]
|
|
// [ "SUP" <oids> ] -- Superior ObjectClasses
|
|
// [ ( "ABSTRACT" | "STRUCTURAL" | "AUXILIARY" )] -- default structural
|
|
// [ "MUST" <oids> ] -- AttributeTypes
|
|
// [ "MAY" <oids> ] -- AttributeTypes
|
|
// ")"
|
|
//
|
|
// Arguments: [LPTSTR] pszObjectClass : The string to parse
|
|
//
|
|
// Returns: [HRESULT] 0 if successful, error HRESULT if not
|
|
//
|
|
// Modifies: pTokenizer (consumes the input buffer)
|
|
//
|
|
// History: 9-3-96 yihsins Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
ObjectClassDescription(
|
|
LPTSTR pszObjectClass,
|
|
PCLASSINFO pClassInfo,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount,
|
|
LPWSTR **pppszNewNames,
|
|
PDWORD pdwNameCount
|
|
)
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
LPWSTR pszTemp;
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
|
|
CSchemaLexer Tokenizer( pszObjectClass );
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken != TOKEN_OPENBRACKET )
|
|
RRETURN(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
|
|
//
|
|
// use TRUE flag as there is a chance that from
|
|
// some schemas, we get bad data that has no GUID
|
|
//
|
|
hr = Oid( &Tokenizer, &(pClassInfo->pszOID), TRUE);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
while ( TRUE ) {
|
|
LPWSTR *ppszDirStrings;
|
|
DWORD dwCount,dwCtr;
|
|
ppszDirStrings = NULL;
|
|
dwCount = 0;
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
Tokenizer.IsKeyword( szToken, &dwToken );
|
|
|
|
switch ( dwToken ) {
|
|
case TOKEN_CLOSEBRACKET:
|
|
RRETURN(S_OK);
|
|
|
|
case TOKEN_NAME:
|
|
hr = DirectoryStrings(
|
|
&Tokenizer,
|
|
&ppszDirStrings,
|
|
&dwCount
|
|
);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (!ppszDirStrings) {
|
|
//
|
|
// We need at least one name.
|
|
//
|
|
BAIL_IF_ERROR(
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA)
|
|
);
|
|
}
|
|
|
|
//
|
|
// For now we will only support the first name in the list.
|
|
//
|
|
pClassInfo->pszName = ppszDirStrings[0];
|
|
|
|
|
|
//
|
|
// The remaining strings will need additional processing
|
|
// in fillClassInfoArray
|
|
//
|
|
*pppszNewNames = ppszDirStrings;
|
|
*pdwNameCount = dwCount;
|
|
|
|
break;
|
|
|
|
case TOKEN_DESC:
|
|
hr = DirectoryString(&Tokenizer,&(pClassInfo->pszDescription));
|
|
break;
|
|
|
|
case TOKEN_OBSOLETE:
|
|
// class is obsolete (RFC 2252)
|
|
pClassInfo->fObsolete = TRUE;
|
|
break;
|
|
|
|
case TOKEN_SUP:
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
Tokenizer.PushBackToken();
|
|
|
|
if ( dwToken == TOKEN_QUOTE )
|
|
{
|
|
|
|
DWORD dwNumStrings = 0;
|
|
LPWSTR *ppszTmp = NULL;
|
|
|
|
while (dwToken == TOKEN_QUOTE) {
|
|
|
|
hr = DirectoryString( &Tokenizer,
|
|
&(pszTemp));
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (dwNumStrings == 0) {
|
|
|
|
pClassInfo->pOIDsSuperiorClasses
|
|
= (LPWSTR *) AllocADsMem(sizeof(LPWSTR) * 2);
|
|
|
|
} else {
|
|
|
|
ppszTmp
|
|
= (LPWSTR *)
|
|
ReallocADsMem(
|
|
pClassInfo->pOIDsSuperiorClasses,
|
|
sizeof(LPWSTR) * (dwNumStrings + 1),
|
|
sizeof(LPWSTR) * (dwNumStrings + 2)
|
|
);
|
|
|
|
pClassInfo->pOIDsSuperiorClasses = ppszTmp;
|
|
}
|
|
|
|
if ( pClassInfo->pOIDsSuperiorClasses == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
pClassInfo->pOIDsSuperiorClasses[dwNumStrings] = pszTemp;
|
|
pClassInfo->pOIDsSuperiorClasses[++dwNumStrings] = NULL;
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
Tokenizer.PushBackToken();
|
|
|
|
} // while
|
|
|
|
} // the token was not a quote
|
|
else {
|
|
hr = Oids(&Tokenizer, &(pClassInfo->pOIDsSuperiorClasses),NULL);
|
|
}
|
|
|
|
break;
|
|
case TOKEN_ABSTRACT:
|
|
pClassInfo->dwType = CLASS_TYPE_ABSTRACT;
|
|
break;
|
|
|
|
case TOKEN_STRUCTURAL:
|
|
pClassInfo->dwType = CLASS_TYPE_STRUCTURAL;
|
|
break;
|
|
|
|
case TOKEN_AUXILIARY:
|
|
pClassInfo->dwType = CLASS_TYPE_AUXILIARY;
|
|
break;
|
|
|
|
case TOKEN_MUST:
|
|
hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMustContain),
|
|
&(pClassInfo->nNumOfMustContain),
|
|
aPropSearchTable, dwSearchTableCount );
|
|
break;
|
|
|
|
case TOKEN_MAY:
|
|
hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMayContain),
|
|
&(pClassInfo->nNumOfMayContain),
|
|
aPropSearchTable, dwSearchTableCount );
|
|
break;
|
|
|
|
case TOKEN_X:
|
|
//
|
|
// This is provider specific info - parse and ignore.
|
|
//
|
|
hr = DirectoryStrings(
|
|
&Tokenizer,
|
|
&ppszDirStrings,
|
|
&dwCount
|
|
);
|
|
//
|
|
// If we could not process this then we need to BAIL
|
|
// as the Tokenizer is not in a recoverable state.
|
|
//
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if (ppszDirStrings) {
|
|
FreeDirectoryStrings(
|
|
ppszDirStrings,
|
|
dwCount
|
|
);
|
|
ppszDirStrings = NULL;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
break;
|
|
}
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: DITContentRuleDescription
|
|
//
|
|
// Synopsis: Parses an DIT content rule description.
|
|
// It parses the following grammar rules
|
|
//
|
|
// <DITContentDescription> ::= "("
|
|
// <oid> -- ObjectClass identifier
|
|
// [ "NAME" <DirectoryStrings> ]
|
|
// [ "DESC" <DirectoryString> ]
|
|
// [ "OBSOLETE" ]
|
|
// [ "AUX" <oids> ] -- Auxiliary ObjectClasses
|
|
// [ "MUST" <oids> ] -- AttributeTypes
|
|
// [ "MAY" <oids> ] -- AttributeTypes
|
|
// [ "NOT" <oids> ] -- AttributeTypes
|
|
// ")"
|
|
//
|
|
// Arguments: [LPTSTR] pszObjectClass : The string to parse
|
|
//
|
|
// Returns: [HRESULT] 0 if successful, error HRESULT if not
|
|
//
|
|
// Modifies: pTokenizer (consumes the input buffer)
|
|
//
|
|
// History: 9-3-96 yihsins Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
DITContentRuleDescription( LPTSTR pszObjectClass, PCLASSINFO pClassInfo,
|
|
SEARCHENTRY *aPropSearchTable,
|
|
DWORD dwSearchTableCount )
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
|
|
CSchemaLexer Tokenizer( pszObjectClass );
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken != TOKEN_OPENBRACKET )
|
|
RRETURN(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
|
|
hr = Oid( &Tokenizer, &(pClassInfo->pszOID));
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
while ( TRUE ) {
|
|
|
|
hr = Tokenizer.GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
Tokenizer.IsKeyword( szToken, &dwToken );
|
|
|
|
switch ( dwToken ) {
|
|
case TOKEN_CLOSEBRACKET:
|
|
RRETURN(S_OK);
|
|
|
|
case TOKEN_NAME:
|
|
hr = DirectoryString( &Tokenizer, NULL);
|
|
// DirectoryStrings
|
|
break;
|
|
|
|
case TOKEN_DESC:
|
|
hr = DirectoryString( &Tokenizer, NULL);
|
|
break;
|
|
|
|
case TOKEN_OBSOLETE:
|
|
// rule is obsolete (RFC 2252)
|
|
pClassInfo->fObsolete = TRUE;
|
|
break;
|
|
|
|
case TOKEN_AUX:
|
|
hr = Oids(&Tokenizer, &(pClassInfo->pOIDsAuxClasses), NULL);
|
|
break;
|
|
|
|
case TOKEN_MUST:
|
|
hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMustContain),
|
|
&(pClassInfo->nNumOfMustContain),
|
|
aPropSearchTable, dwSearchTableCount );
|
|
break;
|
|
|
|
case TOKEN_MAY:
|
|
hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsMayContain),
|
|
&(pClassInfo->nNumOfMayContain),
|
|
aPropSearchTable, dwSearchTableCount );
|
|
break;
|
|
|
|
case TOKEN_NOT:
|
|
hr = PropOids(&Tokenizer, &(pClassInfo->pOIDsNotContain),
|
|
&(pClassInfo->nNumOfNotContain),
|
|
aPropSearchTable, dwSearchTableCount );
|
|
break;
|
|
|
|
default:
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
break;
|
|
}
|
|
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
HRESULT
|
|
Oid(CSchemaLexer * pTokenizer, LPTSTR *ppszOID, BOOL fNoGuid )
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
|
|
*ppszOID = NULL;
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken != TOKEN_IDENTIFIER )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
//
|
|
// Since some people do not like to have
|
|
// an OID on all attributes, we need to work around them.
|
|
// This should be changed once all schemas are compliant
|
|
// AjayR 11-12-98.
|
|
//
|
|
if (fNoGuid && _wcsicmp(szToken, L"NAME") == 0) {
|
|
*ppszOID = AllocADsStr(L"");
|
|
pTokenizer->PushBackToken();
|
|
} else
|
|
*ppszOID = AllocADsStr( szToken );
|
|
|
|
if ( *ppszOID == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
cleanup:
|
|
|
|
if ( FAILED(hr))
|
|
{
|
|
if ( *ppszOID )
|
|
{
|
|
FreeADsStr( *ppszOID );
|
|
*ppszOID = NULL;
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
|
|
}
|
|
|
|
HRESULT
|
|
Oids(CSchemaLexer * pTokenizer, LPTSTR **pOIDs, DWORD *pnNumOfOIDs )
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
DWORD nCount = 0;
|
|
DWORD nCurrent = 0;
|
|
|
|
*pOIDs = NULL;
|
|
if ( pnNumOfOIDs )
|
|
*pnNumOfOIDs = 0;
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
{
|
|
// All classes are subclasses of "top", and hence must contain
|
|
// "objectClass" attribute. Add the "objectClass" attribute here
|
|
// to prevent processing later.
|
|
|
|
nCount = 2;
|
|
|
|
*pOIDs = (LPTSTR *) AllocADsMem( sizeof(LPTSTR) * nCount);
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
(*pOIDs)[nCurrent] = AllocADsStr( szToken );
|
|
if ( (*pOIDs)[nCurrent] == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
(*pOIDs)[++nCurrent] = NULL;
|
|
|
|
}
|
|
else if ( dwToken == TOKEN_OPENBRACKET )
|
|
{
|
|
nCount = 10;
|
|
*pOIDs = (LPTSTR *) AllocADsMem( sizeof(LPTSTR) * nCount);
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
do {
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
{
|
|
if ( nCurrent == nCount )
|
|
{
|
|
*pOIDs = (LPTSTR *) ReallocADsMem( *pOIDs,
|
|
sizeof(LPTSTR) * nCount,
|
|
sizeof(LPTSTR) * nCount * 2);
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
nCount *= 2;
|
|
|
|
}
|
|
|
|
(*pOIDs)[nCurrent] = AllocADsStr( szToken );
|
|
if ( (*pOIDs)[nCurrent] == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
nCurrent++;
|
|
}
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
} while ( dwToken == TOKEN_DOLLAR );
|
|
|
|
if ( dwToken != TOKEN_CLOSEBRACKET )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
if ( nCurrent == nCount )
|
|
{
|
|
// Need one extra NULL entry at the end of the array
|
|
*pOIDs = (LPTSTR *) ReallocADsMem( *pOIDs,
|
|
sizeof(LPTSTR) * nCount,
|
|
sizeof(LPTSTR) * (nCount + 1));
|
|
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
nCount += 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
if ( pnNumOfOIDs )
|
|
*pnNumOfOIDs = nCurrent;
|
|
|
|
cleanup:
|
|
|
|
if ( FAILED(hr))
|
|
{
|
|
if ( *pOIDs )
|
|
{
|
|
for ( DWORD i = 0; i < nCount; i++ )
|
|
{
|
|
if ( (*pOIDs)[i] )
|
|
FreeADsStr( (*pOIDs)[i] );
|
|
}
|
|
FreeADsMem( *pOIDs );
|
|
*pOIDs = NULL;
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
PropOids(CSchemaLexer * pTokenizer, int **pOIDs, DWORD *pnNumOfOIDs,
|
|
SEARCHENTRY *aPropSearchTable, DWORD dwSearchTableCount )
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
DWORD nCount = 0;
|
|
DWORD nCurrent = 0;
|
|
|
|
*pOIDs = NULL;
|
|
if ( pnNumOfOIDs )
|
|
*pnNumOfOIDs = 0;
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
{
|
|
int nIndex = FindSearchTableIndex( szToken,
|
|
aPropSearchTable,
|
|
dwSearchTableCount );
|
|
|
|
if ( nIndex != -1 )
|
|
{
|
|
nCount = 2;
|
|
|
|
*pOIDs = (int *) AllocADsMem( sizeof(int) * nCount);
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
(*pOIDs)[nCurrent] = nIndex;
|
|
(*pOIDs)[++nCurrent] = -1;
|
|
}
|
|
|
|
}
|
|
else if ( dwToken == TOKEN_OPENBRACKET )
|
|
{
|
|
nCount = 10;
|
|
*pOIDs = (int *) AllocADsMem( sizeof(int) * nCount);
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
do {
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_CLOSEBRACKET )
|
|
{
|
|
FreeADsMem( *pOIDs );
|
|
*pOIDs = NULL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
{
|
|
int nIndex = FindSearchTableIndex( szToken,
|
|
aPropSearchTable,
|
|
dwSearchTableCount );
|
|
|
|
if ( nIndex != -1 )
|
|
{
|
|
if ( nCurrent == nCount )
|
|
{
|
|
*pOIDs = (int *) ReallocADsMem( *pOIDs,
|
|
sizeof(int) * nCount,
|
|
sizeof(int) * nCount * 2);
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
nCount *= 2;
|
|
|
|
}
|
|
|
|
(*pOIDs)[nCurrent++] = nIndex;
|
|
}
|
|
|
|
// else we cannot find the property, so skip over it.
|
|
}
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
} while ( dwToken == TOKEN_DOLLAR );
|
|
|
|
if ( dwToken != TOKEN_CLOSEBRACKET )
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
if ( nCurrent == nCount )
|
|
{
|
|
// Need one extra NULL entry at the end of the array
|
|
*pOIDs = (int *) ReallocADsMem( *pOIDs,
|
|
sizeof(int) * nCount,
|
|
sizeof(int) * (nCount + 1));
|
|
|
|
if ( *pOIDs == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
nCount += 1;
|
|
}
|
|
|
|
(*pOIDs)[nCurrent] = -1;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
|
|
if ( pnNumOfOIDs )
|
|
*pnNumOfOIDs = nCurrent;
|
|
|
|
cleanup:
|
|
|
|
if ( FAILED(hr))
|
|
{
|
|
if ( *pOIDs )
|
|
{
|
|
FreeADsMem( *pOIDs );
|
|
*pOIDs = NULL;
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
HRESULT
|
|
DirectoryString(CSchemaLexer * pTokenizer, LPTSTR *ppszDirString )
|
|
{
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
DWORD dwToken;
|
|
HRESULT hr;
|
|
|
|
if ( ppszDirString )
|
|
*ppszDirString = NULL;
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_QUOTE )
|
|
{
|
|
hr = pTokenizer->GetNextToken2(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_IDENTIFIER )
|
|
{
|
|
if ( ppszDirString )
|
|
{
|
|
*ppszDirString = AllocADsStr( szToken );
|
|
if ( *ppszDirString == NULL )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
BAIL_IF_ERROR(hr);
|
|
}
|
|
}
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_IF_ERROR(hr);
|
|
|
|
if ( dwToken == TOKEN_QUOTE )
|
|
RRETURN(S_OK);
|
|
}
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
|
|
cleanup:
|
|
|
|
if ( FAILED(hr))
|
|
{
|
|
if ( ppszDirString && *ppszDirString )
|
|
{
|
|
FreeADsStr( *ppszDirString );
|
|
*ppszDirString = NULL;
|
|
}
|
|
}
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: DirectoryStrings
|
|
//
|
|
// Synopsis: This function is used to process ldap schema elements
|
|
// of the form qdstrings. This is defined in the RFC in detail :
|
|
//
|
|
// space = 1*" "
|
|
// whsp = [ space ]
|
|
// utf8 = <any sequence of octets formed from the UTF-8 [9]
|
|
// transformation of a character from ISO10646 [10]>
|
|
// dstring = 1*utf8
|
|
// qdstring = whsp "'" dstring "'" whsp
|
|
// qdstringlist = [ qdstring *( qdstring ) ]
|
|
// qdstrings = qdstring / ( whsp "(" qdstringlist ")" whsp )
|
|
//
|
|
// Arguments: pTokenizer - The schema lexer object to use.
|
|
// pppszDirStrings - Return value for strings.
|
|
// pdwCount - Return value of number of strings.
|
|
//
|
|
//
|
|
// Returns: HRESULT - S_OK or any failure error code.
|
|
//
|
|
// Modifies: N/A.
|
|
//
|
|
// History: 7-12-2000 AjayR created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
DirectoryStrings(
|
|
CSchemaLexer * pTokenizer,
|
|
LPTSTR **pppszDirStrings,
|
|
PDWORD pdwCount
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szToken[MAX_TOKEN_LENGTH];
|
|
LPWSTR *ppszTmp = NULL;
|
|
LPWSTR pszTmp = NULL;
|
|
DWORD dwToken, dwNumStrings = 0;
|
|
BOOL fNeedCloseBracket = FALSE;
|
|
|
|
ADsAssert(pTokenizer);
|
|
ADsAssert(pdwCount);
|
|
|
|
DWORD dwDummy = sizeof(ADS_ATTR_INFO);
|
|
*pdwCount = 0;
|
|
//
|
|
// Get the token type of the first token.
|
|
//
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (dwToken == TOKEN_OPENBRACKET) {
|
|
//
|
|
// In this case we know that there is more than one string.
|
|
// We can ignore the open bracket and continue to the next
|
|
// token which should be a quote.
|
|
//
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
fNeedCloseBracket = TRUE;
|
|
}
|
|
|
|
//
|
|
// Need to push what should be the quote in either case,
|
|
// back into the tokenizer (only then will the dirString
|
|
// routine work correctly.
|
|
//
|
|
hr = pTokenizer->PushBackToken();
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( dwToken != TOKEN_QUOTE ) {
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
|
|
//
|
|
// While there remain strings to be processed.
|
|
//
|
|
while (dwToken == TOKEN_QUOTE) {
|
|
|
|
hr = DirectoryString(
|
|
pTokenizer,
|
|
&pszTmp
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (dwNumStrings == 0) {
|
|
//
|
|
// Since we NULL terminate the array it should have
|
|
// at least 2 elements in this case.
|
|
//
|
|
ppszTmp = (LPWSTR *) AllocADsMem(sizeof(LPWSTR) * 2);
|
|
if (!ppszTmp) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
else {
|
|
LPWSTR *ppszLocal;
|
|
//
|
|
// To avoid passing the variable itself to local alloc.
|
|
//
|
|
ppszLocal = (LPWSTR *) ReallocADsMem(
|
|
ppszTmp,
|
|
sizeof(LPWSTR) * (dwNumStrings + 1),
|
|
sizeof(LPWSTR) * (dwNumStrings + 2)
|
|
);
|
|
if (ppszLocal) {
|
|
ppszTmp = ppszLocal;
|
|
}
|
|
else {
|
|
//
|
|
// Realloc failed, the old ptr is still valid.
|
|
//
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
ppszTmp[dwNumStrings] = pszTmp;
|
|
ppszTmp[++dwNumStrings] = NULL;
|
|
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
pTokenizer->PushBackToken();
|
|
} // end of while.
|
|
|
|
//
|
|
// If this was qdescrs and not just qdstring.
|
|
//
|
|
if (fNeedCloseBracket) {
|
|
hr = pTokenizer->GetNextToken(szToken, &dwToken);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if (dwToken != TOKEN_CLOSEBRACKET) {
|
|
//
|
|
// Not properly formed - should be just ignore ?
|
|
//
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// The count is the actual number not including the NULL string.
|
|
//
|
|
*pdwCount = dwNumStrings;
|
|
*pppszDirStrings = ppszTmp;
|
|
|
|
error:
|
|
if (FAILED(hr)) {
|
|
if (ppszTmp) {
|
|
//
|
|
// Free the strings if any and then the array itself.
|
|
//
|
|
for (DWORD dwCount = 0; dwCount < dwNumStrings; dwCount++) {
|
|
if (ppszTmp[dwCount]) {
|
|
FreeADsStr(ppszTmp[dwCount]);
|
|
}
|
|
}
|
|
FreeADsMem(ppszTmp);
|
|
ppszTmp = NULL;
|
|
}
|
|
//
|
|
// Need to reset the count.
|
|
//
|
|
*pdwCount = 0;
|
|
}
|
|
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: FreeDirectoryStrings
|
|
//
|
|
// Synopsis: This function is used to free the entries returned from
|
|
// the DirectoryStrings routine.
|
|
//
|
|
// Arguments: ppszDirStrings - List of strings to free.
|
|
// dwCount - Number of strings to free.
|
|
// fSkipFirstElement - If true, do not free the 1st element.
|
|
//
|
|
//
|
|
// Returns: N/A.
|
|
//
|
|
// Modifies: ppszDirStrings contents is freed including the array.
|
|
//
|
|
// History: 8-01-2000 AjayR created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void FreeDirectoryStrings(
|
|
LPTSTR *ppszDirStrings,
|
|
DWORD dwCount,
|
|
DWORD dwElementsToFree
|
|
)
|
|
{
|
|
DWORD dwCtr;
|
|
if (!ppszDirStrings) {
|
|
return;
|
|
}
|
|
|
|
switch (dwElementsToFree) {
|
|
|
|
case FREE_ALL_BUT_FIRST :
|
|
dwCtr = 1;
|
|
break;
|
|
|
|
case FREE_ALL :
|
|
dwCtr = 0;
|
|
break;
|
|
|
|
case FREE_ARRAY_NOT_ELEMENTS :
|
|
dwCtr = dwCount;
|
|
break;
|
|
}
|
|
|
|
for (; dwCtr < dwCount; dwCtr++) {
|
|
if (ppszDirStrings[dwCtr]) {
|
|
FreeADsStr(ppszDirStrings[dwCtr]);
|
|
ppszDirStrings[dwCtr] = NULL;
|
|
}
|
|
}
|
|
|
|
FreeADsMem(ppszDirStrings);
|
|
return;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: AddNewNamesToPropertyArray --- Helper function.
|
|
//
|
|
// Synopsis: This function adds new entries to the property info array.
|
|
// Specifically, this fn is called when there are multiple names
|
|
// associated with the description of a single property. The new
|
|
// entries will have the same information as the current element
|
|
// but the appropriate new name.
|
|
//
|
|
// Arguments: ppPropArray - Property array containing current
|
|
// elements. This array is updated to contain
|
|
// the new elements on success and is
|
|
// untouched otherwise.
|
|
// dwCurPos - The current element being processed.
|
|
// dwCount - The current count of elements.
|
|
// ppszNewNames - Array of names to add (1st element is
|
|
// already a part of the property array).
|
|
//
|
|
// Returns: S_OK or E_OUTOFMEMORY.
|
|
//
|
|
// Modifies: *ppPropArray is modified in all success cases and some failure
|
|
// cases (realloc succeeds but not the subsequent string allocs).
|
|
//
|
|
// History: 10-03-2000 AjayR created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
AddNewNamesToPropertyArray(
|
|
PROPERTYINFO **ppPropArray,
|
|
DWORD dwCurPos,
|
|
DWORD dwCount,
|
|
LPWSTR *ppszNewNames,
|
|
DWORD dwNewNameCount
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PPROPERTYINFO pNewPropArray = NULL;
|
|
DWORD dwAdditions = 0;
|
|
DWORD dwCurCount = dwCount;
|
|
|
|
//
|
|
// The first element is already in the array.
|
|
//
|
|
dwAdditions = --dwNewNameCount;
|
|
|
|
if (!dwNewNameCount) {
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//
|
|
// We need to realloc the new array and copy over the new elements.
|
|
//
|
|
pNewPropArray = (PROPERTYINFO *)
|
|
ReallocADsMem(
|
|
*ppPropArray,
|
|
(dwCurCount) * sizeof(PROPERTYINFO),
|
|
(dwCurCount + dwAdditions) * sizeof(PROPERTYINFO)
|
|
);
|
|
if (!pNewPropArray) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// If the alloc succeeded we must return the new array.
|
|
//
|
|
*ppPropArray = pNewPropArray;
|
|
|
|
for (DWORD dwCtr = 0; dwCtr < dwAdditions; dwCtr++ ) {
|
|
PROPERTYINFO propOriginal = pNewPropArray[dwCurPos];
|
|
PROPERTYINFO *pPropNew = pNewPropArray + (dwCurPos + dwCtr + 1);
|
|
|
|
//
|
|
// Copy over the property. First all data that is not ptrs.
|
|
//
|
|
pPropNew->lMaxRange = propOriginal.lMaxRange;
|
|
pPropNew->lMinRange = propOriginal.lMinRange;
|
|
pPropNew->fSingleValued = propOriginal.fSingleValued;
|
|
|
|
pPropNew->fObsolete = propOriginal.fObsolete;
|
|
pPropNew->fCollective = propOriginal.fCollective;
|
|
pPropNew->fDynamic = propOriginal.fDynamic;
|
|
|
|
pPropNew->fNoUserModification = propOriginal.fNoUserModification;
|
|
pPropNew->dwUsage = propOriginal.dwUsage;
|
|
pPropNew->fProcessedSuperiorClass = propOriginal.fProcessedSuperiorClass;
|
|
|
|
//
|
|
// Now the strings.
|
|
//
|
|
pPropNew->pszOID = AllocADsStr(propOriginal.pszOID);
|
|
if (propOriginal.pszOID && !pPropNew->pszOID) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPropNew->pszSyntax = AllocADsStr(propOriginal.pszSyntax);
|
|
if (propOriginal.pszSyntax && !pPropNew->pszSyntax) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPropNew->pszDescription = AllocADsStr(propOriginal.pszDescription);
|
|
if (propOriginal.pszDescription && !pPropNew->pszDescription) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPropNew->pszOIDSup = AllocADsStr(propOriginal.pszOIDSup);
|
|
if (propOriginal.pszOIDSup && !pPropNew->pszOIDSup) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPropNew->pszOIDEquality = AllocADsStr(propOriginal.pszOIDEquality);
|
|
if (propOriginal.pszOIDEquality && !pPropNew->pszOIDEquality) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPropNew->pszOIDOrdering = AllocADsStr(propOriginal.pszOIDOrdering);
|
|
if (propOriginal.pszOIDOrdering && !pPropNew->pszOIDOrdering) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pPropNew->pszOIDSubstr = AllocADsStr(propOriginal.pszOIDSubstr);
|
|
if (propOriginal.pszOIDSubstr && !pPropNew->pszOIDSubstr) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// This is just a copy from the array.
|
|
//
|
|
pPropNew->pszPropertyName = ppszNewNames[dwCtr + 1];
|
|
}
|
|
|
|
//
|
|
// Success case.
|
|
//
|
|
RRETURN(hr);
|
|
|
|
error:
|
|
|
|
//
|
|
// Something failed, try and cleanup some pieces
|
|
//
|
|
if (pNewPropArray && (dwCtr != (DWORD)-1) ) {
|
|
//
|
|
// Cleanup the new elements added.
|
|
//
|
|
for (DWORD i = 0; i <= dwCtr; i++) {
|
|
PROPERTYINFO *pPropFree = pNewPropArray + (dwCurPos + i + 1);
|
|
//
|
|
// Free all the strings in this element except name.
|
|
//
|
|
if (pPropFree->pszOID) {
|
|
FreeADsStr(pPropFree->pszOID);
|
|
pPropFree->pszOID = NULL;
|
|
}
|
|
|
|
if (pPropFree->pszSyntax) {
|
|
FreeADsStr(pPropFree->pszSyntax);
|
|
pPropFree->pszSyntax = NULL;
|
|
}
|
|
|
|
if (pPropFree->pszDescription) {
|
|
FreeADsStr(pPropFree->pszDescription);
|
|
pPropFree->pszDescription = NULL;
|
|
}
|
|
|
|
if (pPropFree->pszOIDSup) {
|
|
FreeADsStr(pPropFree->pszOIDSup);
|
|
pPropFree->pszOIDSup = NULL;
|
|
}
|
|
|
|
if (pPropFree->pszOIDEquality) {
|
|
FreeADsStr(pPropFree->pszOIDEquality);
|
|
pPropFree->pszOIDEquality = NULL;
|
|
}
|
|
|
|
if (pPropFree->pszOIDOrdering) {
|
|
FreeADsStr(pPropFree->pszOIDOrdering);
|
|
pPropFree->pszOIDOrdering = NULL;
|
|
}
|
|
|
|
if (pPropFree->pszOIDSubstr) {
|
|
FreeADsStr(pPropFree->pszOIDSubstr);
|
|
pPropFree->pszOIDSubstr = NULL;
|
|
}
|
|
} // for
|
|
} // if we have elements to free
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function: AddNewNamesToClassArray --- Helper function.
|
|
//
|
|
// Synopsis: This function adds new entries to the class info array.
|
|
// Specifically, this fn is called when there are multiple names
|
|
// associated with the description of a single class. The new
|
|
// entries will have the same information as the current element
|
|
// but the appropriate new name.
|
|
//
|
|
// Arguments: ppClassArray - Class array containing current
|
|
// elements. This array is updated to contain
|
|
// the new elements on success and is
|
|
// untouched otherwise.
|
|
// dwCurPos - The current element being processed.
|
|
// dwCount - The current count of elements.
|
|
// ppszNewNames - Array of names to add (1st element is
|
|
// already a part of the property array).
|
|
// dwNewNameCount - Number of elements in the new array.
|
|
//
|
|
// Returns: N/A.
|
|
//
|
|
// Modifies: *ppClassArray always on success and in some failure cases.
|
|
//
|
|
// History: 10-06-2000 AjayR created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
AddNewNamesToClassArray(
|
|
CLASSINFO **ppClassArray,
|
|
DWORD dwCurPos,
|
|
DWORD dwCount,
|
|
LPWSTR *ppszNewNames,
|
|
DWORD dwNewNameCount
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
PCLASSINFO pNewClassArray = NULL;
|
|
DWORD dwAdditions = 0;
|
|
DWORD dwCurCount = dwCount;
|
|
int nCount;
|
|
|
|
//
|
|
// The first element is already in the array.
|
|
//
|
|
dwAdditions = --dwNewNameCount;
|
|
|
|
if (!dwNewNameCount) {
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//
|
|
// We need to realloc the new array and copy over the new elements.
|
|
//
|
|
pNewClassArray = (CLASSINFO *)
|
|
ReallocADsMem(
|
|
*ppClassArray,
|
|
(dwCurCount) * sizeof(CLASSINFO),
|
|
(dwCurCount + dwAdditions) * sizeof(CLASSINFO)
|
|
);
|
|
if (!pNewClassArray) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// If the alloc succeeded we must return the new array.
|
|
//
|
|
*ppClassArray = pNewClassArray;
|
|
|
|
for (DWORD dwCtr = 0; dwCtr < dwAdditions; dwCtr++ ) {
|
|
CLASSINFO classOriginal = pNewClassArray[dwCurPos];
|
|
CLASSINFO *pClassNew = pNewClassArray + (dwCurPos + dwCtr + 1);
|
|
|
|
//
|
|
// Copy over the property. First all data that is not ptrs.
|
|
//
|
|
pClassNew->dwType = classOriginal.dwType;
|
|
pClassNew->lHelpFileContext = classOriginal.lHelpFileContext;
|
|
pClassNew->fObsolete = classOriginal.fObsolete;
|
|
|
|
pClassNew->fProcessedSuperiorClasses =
|
|
classOriginal.fProcessedSuperiorClasses;
|
|
pClassNew->IsContainer = classOriginal.IsContainer;
|
|
|
|
//
|
|
// Now the strings and other pointers.
|
|
//
|
|
pClassNew->pszOID = AllocADsStr(classOriginal.pszOID);
|
|
if (classOriginal.pszOID && !pClassNew->pszOID) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// The GUIDs are not copied over as they are not used or freed.
|
|
//
|
|
|
|
pClassNew->pszHelpFileName = AllocADsStr(classOriginal.pszHelpFileName);
|
|
if (classOriginal.pszHelpFileName && !pClassNew->pszHelpFileName) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
pClassNew->pszDescription = AllocADsStr(classOriginal.pszDescription);
|
|
if (classOriginal.pszDescription && !pClassNew->pszDescription) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
//
|
|
// pOIDsSuperiorClasses and pOIDsAuxClasses are arrays.
|
|
//
|
|
if (classOriginal.pOIDsSuperiorClasses) {
|
|
pClassNew->pOIDsSuperiorClasses =
|
|
CopyStringArray(classOriginal.pOIDsSuperiorClasses);
|
|
|
|
if (!pClassNew->pOIDsSuperiorClasses) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
if (classOriginal.pOIDsAuxClasses) {
|
|
pClassNew->pOIDsAuxClasses =
|
|
CopyStringArray(classOriginal.pOIDsAuxClasses);
|
|
|
|
if (!pClassNew->pOIDsAuxClasses) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now the int arrays. Note that all of these will tag on the
|
|
// the last element (-1), -1 is not included in the count.
|
|
//
|
|
if (classOriginal.pOIDsMustContain) {
|
|
|
|
nCount = classOriginal.nNumOfMustContain + 1;
|
|
pClassNew->pOIDsMustContain =
|
|
(int *) AllocADsMem(sizeof(int) * nCount);
|
|
|
|
if (!pClassNew->pOIDsMustContain) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
memcpy(
|
|
pClassNew->pOIDsMustContain,
|
|
classOriginal.pOIDsMustContain,
|
|
sizeof(int) * nCount
|
|
);
|
|
|
|
pClassNew->nNumOfMustContain = --nCount;
|
|
}
|
|
|
|
if (classOriginal.pOIDsMayContain) {
|
|
|
|
nCount = classOriginal.nNumOfMayContain + 1;
|
|
pClassNew->pOIDsMayContain =
|
|
(int *) AllocADsMem(sizeof(int) * nCount);
|
|
|
|
if (!pClassNew->pOIDsMayContain) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
memcpy(
|
|
pClassNew->pOIDsMayContain,
|
|
classOriginal.pOIDsMayContain,
|
|
sizeof(int) * nCount
|
|
);
|
|
pClassNew->nNumOfMayContain = --nCount;
|
|
}
|
|
|
|
if (classOriginal.pOIDsNotContain) {
|
|
|
|
nCount = classOriginal.nNumOfNotContain + 1;
|
|
pClassNew->pOIDsNotContain =
|
|
(int *) AllocADsMem(sizeof(int) * nCount);
|
|
|
|
if (!pClassNew->pOIDsNotContain) {
|
|
BAIL_ON_FAILURE(hr = E_OUTOFMEMORY);
|
|
}
|
|
|
|
memcpy(
|
|
pClassNew->pOIDsNotContain,
|
|
classOriginal.pOIDsNotContain,
|
|
sizeof(int) * nCount
|
|
);
|
|
pClassNew->nNumOfNotContain = --nCount;
|
|
}
|
|
|
|
|
|
//
|
|
// This is just a copy from the array.
|
|
//
|
|
pClassNew->pszName = ppszNewNames[dwCtr + 1];
|
|
}
|
|
|
|
//
|
|
// Success case.
|
|
//
|
|
RRETURN(hr);
|
|
|
|
error:
|
|
|
|
//
|
|
// Something failed, try and cleanup some pieces
|
|
//
|
|
if (pNewClassArray && (dwCtr != (DWORD)-1) ) {
|
|
//
|
|
// Cleanup the new elements added.
|
|
//
|
|
for (DWORD i = 0; i <= dwCtr; i++) {
|
|
CLASSINFO *pClassFree = pNewClassArray + (dwCurPos + i + 1);
|
|
//
|
|
// Free all the strings in this element except name.
|
|
//
|
|
if (pClassFree->pszOID) {
|
|
FreeADsStr(pClassFree->pszOID);
|
|
pClassFree->pszOID = NULL;
|
|
}
|
|
|
|
if (pClassFree->pszHelpFileName) {
|
|
FreeADsStr(pClassFree->pszHelpFileName);
|
|
pClassFree->pszHelpFileName = NULL;
|
|
}
|
|
|
|
if (pClassFree->pszDescription) {
|
|
FreeADsStr(pClassFree->pszDescription);
|
|
pClassFree->pszDescription = NULL;
|
|
}
|
|
|
|
//
|
|
// Now the string arrays.
|
|
//
|
|
if (pClassFree->pOIDsSuperiorClasses) {
|
|
nCount = 0;
|
|
LPTSTR pszTemp;
|
|
|
|
while (pszTemp = (pClassFree->pOIDsSuperiorClasses)[nCount]) {
|
|
FreeADsStr(pszTemp);
|
|
nCount++;
|
|
}
|
|
|
|
FreeADsMem(pClassFree->pOIDsSuperiorClasses);
|
|
pClassFree->pOIDsSuperiorClasses = NULL;
|
|
}
|
|
|
|
if (pClassFree->pOIDsAuxClasses) {
|
|
nCount = 0;
|
|
LPTSTR pszTemp;
|
|
|
|
while (pszTemp = (pClassFree->pOIDsAuxClasses)[nCount]) {
|
|
FreeADsStr(pszTemp);
|
|
nCount++;
|
|
}
|
|
FreeADsMem(pClassFree->pOIDsAuxClasses);
|
|
pClassFree->pOIDsAuxClasses = NULL;
|
|
}
|
|
|
|
if (pClassFree->pOIDsMustContain) {
|
|
FreeADsMem(pClassFree->pOIDsMustContain);
|
|
pClassFree->pOIDsMustContain = NULL;
|
|
pClassFree->nNumOfMustContain = 0;
|
|
}
|
|
|
|
if (pClassFree->pOIDsMayContain) {
|
|
FreeADsMem(pClassFree->pOIDsMayContain);
|
|
pClassFree->pOIDsMayContain = NULL;
|
|
pClassFree->nNumOfMayContain = 0;
|
|
}
|
|
|
|
if (pClassFree->pOIDsNotContain) {
|
|
FreeADsMem(pClassFree->pOIDsNotContain);
|
|
pClassFree->pOIDsNotContain = NULL;
|
|
pClassFree->nNumOfNotContain = 0;
|
|
}
|
|
|
|
} // for
|
|
} // if we have elements to free
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CSchemaLexer::CSchemaLexer(LPTSTR szBuffer):
|
|
_ptr(NULL),
|
|
_Buffer(NULL),
|
|
_dwLastTokenLength(0),
|
|
_dwLastToken(0),
|
|
_dwEndofString(0),
|
|
_fInQuotes(FALSE)
|
|
{
|
|
if (!szBuffer || !*szBuffer) {
|
|
return;
|
|
}
|
|
_Buffer = AllocADsStr(szBuffer);
|
|
_ptr = _Buffer;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 08-12-96 t-danal Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CSchemaLexer::~CSchemaLexer()
|
|
{
|
|
FreeADsStr(_Buffer);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CSchemaLexer::GetNextToken(LPTSTR szToken, LPDWORD pdwToken)
|
|
{
|
|
TCHAR c;
|
|
DWORD state = 0;
|
|
LPTSTR pch = szToken;
|
|
|
|
memset(szToken, 0, sizeof(TCHAR) * MAX_TOKEN_LENGTH);
|
|
_dwLastTokenLength = 0;
|
|
|
|
while (1) {
|
|
c = NextChar();
|
|
switch (state) {
|
|
|
|
case 0:
|
|
*pch++ = c;
|
|
_dwLastTokenLength++;
|
|
|
|
switch (c) {
|
|
case TEXT('(') :
|
|
*pdwToken = TOKEN_OPENBRACKET;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT(')') :
|
|
*pdwToken = TOKEN_CLOSEBRACKET;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('\'') :
|
|
*pdwToken = TOKEN_QUOTE;
|
|
_dwLastToken = *pdwToken;
|
|
_fInQuotes = !_fInQuotes;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('$') :
|
|
*pdwToken = TOKEN_DOLLAR;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT(' ') :
|
|
pch--;
|
|
_dwLastTokenLength--;
|
|
break;
|
|
|
|
case TEXT('\0') :
|
|
*pdwToken = TOKEN_END;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('{') :
|
|
*pdwToken = TOKEN_OPEN_CURLY;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('}') :
|
|
*pdwToken = TOKEN_CLOSE_CURLY;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
default:
|
|
state = 1;
|
|
break;
|
|
|
|
} // end of switch c
|
|
break;
|
|
|
|
case 1:
|
|
switch (c) {
|
|
case TEXT('(') :
|
|
case TEXT(')') :
|
|
case TEXT('\'') :
|
|
case TEXT('$') :
|
|
case TEXT(' ') :
|
|
case TEXT('\0') :
|
|
case TEXT('{') :
|
|
case TEXT('}') :
|
|
|
|
if ( _fInQuotes && c != TEXT('\''))
|
|
{
|
|
if ( c == TEXT('\0'))
|
|
RRETURN(E_FAIL);
|
|
|
|
*pch++ = c;
|
|
_dwLastTokenLength++;
|
|
state = 1;
|
|
break;
|
|
}
|
|
else // Not in quotes or in quotes and reach the matching quote
|
|
{
|
|
PushbackChar();
|
|
*pdwToken = TOKEN_IDENTIFIER;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN (S_OK);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
*pch++ = c;
|
|
_dwLastTokenLength++;
|
|
state = 1;
|
|
break;
|
|
} // switch c
|
|
|
|
break;
|
|
|
|
default:
|
|
RRETURN(E_FAIL);
|
|
} // switch state
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
CSchemaLexer::GetNextToken2(LPTSTR szToken, LPDWORD pdwToken)
|
|
{
|
|
TCHAR c;
|
|
DWORD state = 0;
|
|
LPTSTR pch = szToken;
|
|
|
|
memset(szToken, 0, sizeof(TCHAR) * MAX_TOKEN_LENGTH);
|
|
_dwLastTokenLength = 0;
|
|
|
|
while (1) {
|
|
c = NextChar();
|
|
switch (state) {
|
|
|
|
case 0:
|
|
*pch++ = c;
|
|
_dwLastTokenLength++;
|
|
|
|
switch (c) {
|
|
case TEXT('(') :
|
|
*pdwToken = TOKEN_OPENBRACKET;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT(')') :
|
|
*pdwToken = TOKEN_CLOSEBRACKET;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('\'') :
|
|
*pdwToken = TOKEN_QUOTE;
|
|
_dwLastToken = *pdwToken;
|
|
_fInQuotes = !_fInQuotes;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('$') :
|
|
*pdwToken = TOKEN_DOLLAR;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('\0') :
|
|
*pdwToken = TOKEN_END;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('{') :
|
|
*pdwToken = TOKEN_OPEN_CURLY;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
case TEXT('}') :
|
|
*pdwToken = TOKEN_CLOSE_CURLY;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN(S_OK);
|
|
break;
|
|
|
|
default:
|
|
state = 1;
|
|
break;
|
|
|
|
} // end of switch c
|
|
break;
|
|
|
|
case 1:
|
|
switch (c) {
|
|
case TEXT('(') :
|
|
case TEXT(')') :
|
|
case TEXT('\'') :
|
|
case TEXT('$') :
|
|
case TEXT(' ') :
|
|
case TEXT('\0') :
|
|
case TEXT('{') :
|
|
case TEXT('}') :
|
|
|
|
if ( _fInQuotes && c != TEXT('\''))
|
|
{
|
|
if ( c == TEXT('\0'))
|
|
RRETURN(E_FAIL);
|
|
|
|
*pch++ = c;
|
|
_dwLastTokenLength++;
|
|
state = 1;
|
|
break;
|
|
}
|
|
else // Not in quotes or in quotes and reach the matching quote
|
|
{
|
|
PushbackChar();
|
|
*pdwToken = TOKEN_IDENTIFIER;
|
|
_dwLastToken = *pdwToken;
|
|
RRETURN (S_OK);
|
|
}
|
|
break;
|
|
|
|
default :
|
|
*pch++ = c;
|
|
_dwLastTokenLength++;
|
|
state = 1;
|
|
break;
|
|
} // switch c
|
|
|
|
break;
|
|
|
|
default:
|
|
RRETURN(E_FAIL);
|
|
} // switch state
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
TCHAR
|
|
CSchemaLexer::NextChar()
|
|
{
|
|
if (_ptr == NULL || *_ptr == TEXT('\0')) {
|
|
_dwEndofString = TRUE;
|
|
return(TEXT('\0'));
|
|
}
|
|
return(*_ptr++);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis: ONLY ONE TOKEN CAN BE PUSHED BACK.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CSchemaLexer::PushBackToken()
|
|
{
|
|
|
|
DWORD i = 0;
|
|
|
|
if (_dwLastToken == TOKEN_END) {
|
|
RRETURN(S_OK);
|
|
}
|
|
|
|
for (i=0; i < _dwLastTokenLength; i++) {
|
|
if (*(--_ptr) == TEXT('\'') ) {
|
|
_fInQuotes = !_fInQuotes;
|
|
}
|
|
}
|
|
|
|
RRETURN(S_OK);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CSchemaLexer::PushbackChar()
|
|
{
|
|
if (_dwEndofString) {
|
|
return;
|
|
}
|
|
_ptr--;
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
// Function:
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Modifies:
|
|
//
|
|
// History: 11-3-95 krishnag Created.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
CSchemaLexer::IsKeyword(LPTSTR szToken, LPDWORD pdwToken)
|
|
{
|
|
DWORD i = 0;
|
|
|
|
for (i = 0; i < g_dwSchemaKeywordListSize; i++) {
|
|
if (!_tcsicmp(szToken, g_aSchemaKeywordList[i].Keyword)) {
|
|
*pdwToken = g_aSchemaKeywordList[i].dwTokenId;
|
|
return(TRUE);
|
|
}
|
|
else if (!_wcsnicmp(szToken, L"X-", 2)) {
|
|
//
|
|
// Terms begining with X- are special tokens for schema.
|
|
//
|
|
*pdwToken = TOKEN_X;
|
|
return(TRUE);
|
|
}
|
|
}
|
|
*pdwToken = 0;
|
|
return(FALSE);
|
|
}
|
|
|
|
int _cdecl searchentrycmp( const void *s1, const void *s2 )
|
|
{
|
|
SEARCHENTRY *srch1 = (SEARCHENTRY *) s1;
|
|
SEARCHENTRY *srch2 = (SEARCHENTRY *) s2;
|
|
|
|
return ( _tcsicmp( srch1->pszName, srch2->pszName ));
|
|
}
|
|
|
|
int _cdecl intcmp( const void *s1, const void *s2 )
|
|
{
|
|
int n1 = *((int *) s1);
|
|
int n2 = *((int *) s2);
|
|
int retval;
|
|
|
|
if ( n1 == n2 )
|
|
retval = 0;
|
|
else if ( n1 < n2 )
|
|
retval = -1;
|
|
else
|
|
retval = 1;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
long CompareUTCTime(
|
|
LPTSTR pszTime1,
|
|
LPTSTR pszTime2
|
|
)
|
|
{
|
|
SYSTEMTIME sysTime1;
|
|
SYSTEMTIME sysTime2;
|
|
FILETIME fTime1;
|
|
FILETIME fTime2;
|
|
|
|
memset( &sysTime1, 0, sizeof(sysTime1));
|
|
memset( &sysTime2, 0, sizeof(sysTime2));
|
|
|
|
//
|
|
// We are ignoring the last part which might be a float.
|
|
// The time window is sufficiently small for us not to
|
|
// worry about this value.
|
|
//
|
|
_stscanf( pszTime1, TEXT("%4d%2d%2d%2d%2d%2d"),
|
|
&sysTime1.wYear,
|
|
&sysTime1.wMonth,
|
|
&sysTime1.wDay,
|
|
&sysTime1.wHour,
|
|
&sysTime1.wMinute,
|
|
&sysTime1.wSecond
|
|
);
|
|
|
|
_stscanf( pszTime2, TEXT("%4d%2d%2d%2d%2d%2d"),
|
|
&sysTime2.wYear,
|
|
&sysTime2.wMonth,
|
|
&sysTime2.wDay,
|
|
&sysTime2.wHour,
|
|
&sysTime2.wMinute,
|
|
&sysTime2.wSecond
|
|
);
|
|
|
|
if ( SystemTimeToFileTime( &sysTime1, &fTime1 )
|
|
&& SystemTimeToFileTime( &sysTime2, &fTime2 )
|
|
)
|
|
{
|
|
return CompareFileTime( &fTime1, &fTime2 );
|
|
}
|
|
|
|
// If SystemTimeToFileTime failed, then assume that pszTime1 is in cache,
|
|
// pszTime2 is on the server and if we cannot get the correct time, we
|
|
// should always read from the server again. Hence, return -1;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
int FindEntryInSearchTable( LPTSTR pszName, SEARCHENTRY *aSearchTable, DWORD nSearchTableSize)
|
|
{
|
|
SEARCHENTRY searchEntry;
|
|
SEARCHENTRY *matchedEntry = NULL;
|
|
|
|
searchEntry.pszName = pszName;
|
|
matchedEntry = (SEARCHENTRY *) bsearch(
|
|
(SEARCHENTRY *) &searchEntry,
|
|
aSearchTable, nSearchTableSize,
|
|
sizeof(SEARCHENTRY), searchentrycmp );
|
|
|
|
if ( matchedEntry )
|
|
{
|
|
return matchedEntry->nIndex;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int FindSearchTableIndex( LPTSTR pszName, SEARCHENTRY *aSearchTable, DWORD nSearchTableSize )
|
|
{
|
|
SEARCHENTRY searchEntry;
|
|
SEARCHENTRY *matchedEntry = NULL;
|
|
|
|
searchEntry.pszName = pszName;
|
|
matchedEntry = (SEARCHENTRY *) bsearch(
|
|
(SEARCHENTRY *) &searchEntry,
|
|
aSearchTable, nSearchTableSize,
|
|
sizeof(SEARCHENTRY), searchentrycmp );
|
|
|
|
if ( matchedEntry )
|
|
{
|
|
return (int)( matchedEntry - aSearchTable ); // return index of search table
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ReadSubSchemaSubEntry(
|
|
LPWSTR pszLDAPServer,
|
|
LPWSTR * ppszSubSchemaEntry,
|
|
OUT BOOL *pfBoundOk, // have we at least once bound to domain
|
|
// successfully, OPTIONAL (can be NULL)
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(ppszSubSchemaEntry);
|
|
|
|
*ppszSubSchemaEntry = NULL;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
pfBoundOk,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( !rootDSE.pszSubSchemaEntry ) {
|
|
//
|
|
// SubschemaEntry must be found
|
|
//
|
|
|
|
BAIL_ON_FAILURE(hr = E_FAIL);
|
|
}
|
|
else {
|
|
*ppszSubSchemaEntry = rootDSE.pszSubSchemaEntry;
|
|
}
|
|
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ReadPagingSupportedAttr(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfPagingSupported,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfPagingSupported);
|
|
*pfPagingSupported = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfPagingSupported = rootDSE.fPagingSupported;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ReadSortingSupportedAttr(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfSortingSupported,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfSortingSupported);
|
|
*pfSortingSupported = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfSortingSupported = rootDSE.fSortingSupported;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ReadAttribScopedSupportedAttr(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfAttribScopedSupported,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfAttribScopedSupported);
|
|
*pfAttribScopedSupported = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfAttribScopedSupported = rootDSE.fAttribScopedSupported;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
ReadVLVSupportedAttr(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfVLVSupported,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfVLVSupported);
|
|
*pfVLVSupported = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfVLVSupported = rootDSE.fVLVSupported;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// Returns the info about SecDesc Control if appropriate
|
|
//
|
|
HRESULT
|
|
ReadSecurityDescriptorControlType(
|
|
LPWSTR pszLDAPServer,
|
|
DWORD * pdwSecDescType,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pdwSecDescType);
|
|
*pdwSecDescType = ADSI_LDAPC_SECDESC_NONE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pdwSecDescType = rootDSE.dwSecDescType;
|
|
|
|
error:
|
|
|
|
//
|
|
// Since the error case is uninteresting, if there was an
|
|
// error, we will continue with no sec desc
|
|
//
|
|
if (hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE))
|
|
RRETURN (S_OK);
|
|
else
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// This is to see if we support the domain scope control.
|
|
// If we do we can set it to reduce server load.
|
|
//
|
|
HRESULT
|
|
ReadDomScopeSupportedAttr(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfDomScopeSupported,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfDomScopeSupported);
|
|
*pfDomScopeSupported = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfDomScopeSupported = rootDSE.fDomScopeSupported;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|
|
//
|
|
// This is to see if we support the domain scope control.
|
|
// If we do we can set it to reduce server load.
|
|
//
|
|
HRESULT
|
|
ReadServerSupportsIsADControl(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfServerIsAD,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfServerIsAD);
|
|
*pfServerIsAD = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfServerIsAD = rootDSE.fTalkingToAD;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//
|
|
// This is to see if we are talking to enhacned AD servers so we
|
|
// can process the aux classes correctly.
|
|
//
|
|
HRESULT
|
|
ReadServerSupportsIsEnhancedAD(
|
|
LPWSTR pszLDAPServer,
|
|
BOOL * pfServerIsEnhancedAD,
|
|
BOOL * pfServerIsADControl,
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ROOTDSENODE rootDSE = {0};
|
|
|
|
ADsAssert(pfServerIsEnhancedAD);
|
|
ADsAssert(pfServerIsADControl);
|
|
|
|
*pfServerIsEnhancedAD = FALSE;
|
|
*pfServerIsADControl = FALSE;
|
|
|
|
//
|
|
// Call the generic function
|
|
//
|
|
|
|
hr = ReadRootDSENode(
|
|
pszLDAPServer,
|
|
&rootDSE,
|
|
NULL,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
if ( rootDSE.pszSubSchemaEntry) {
|
|
|
|
FreeADsStr (rootDSE.pszSubSchemaEntry);
|
|
}
|
|
|
|
*pfServerIsEnhancedAD = rootDSE.fTalkingToEnhancedAD;
|
|
*pfServerIsADControl = rootDSE.fTalkingToAD;
|
|
|
|
error:
|
|
|
|
RRETURN(hr);
|
|
}
|
|
|
|
BOOL
|
|
EquivalentServers(
|
|
LPWSTR pszTargetServer,
|
|
LPWSTR pszSourceServer
|
|
)
|
|
{
|
|
if (!pszTargetServer && !pszSourceServer) {
|
|
return(TRUE);
|
|
}
|
|
|
|
if (pszTargetServer && pszSourceServer) {
|
|
|
|
#ifdef WIN95
|
|
if (!_wcsicmp(pszTargetServer, pszSourceServer)) {
|
|
#else
|
|
if (CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pszTargetServer,
|
|
-1,
|
|
pszSourceServer,
|
|
-1
|
|
) == CSTR_EQUAL ) {
|
|
#endif
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
BOOL
|
|
EquivalentUsers(
|
|
LPWSTR pszUser1,
|
|
LPWSTR pszUser2
|
|
)
|
|
{
|
|
if (!pszUser1 && !pszUser2) {
|
|
return(TRUE);
|
|
}
|
|
|
|
if (pszUser1 && pszUser2) {
|
|
|
|
#ifdef WIN95
|
|
if (!_wcsicmp(pszUser1, pszUser2)) {
|
|
#else
|
|
if (CompareStringW(
|
|
LOCALE_SYSTEM_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pszUser1,
|
|
-1,
|
|
pszUser2,
|
|
-1
|
|
) == CSTR_EQUAL ) {
|
|
#endif
|
|
return(TRUE);
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
HRESULT
|
|
ReadRootDSENode(
|
|
LPWSTR pszLDAPServer,
|
|
PROOTDSENODE pRootDSE,
|
|
OUT BOOL * pfBoundOk, // have we at least once bound to domain
|
|
// successfully, OPTIONAL (can be NULL)
|
|
CCredentials& Credentials,
|
|
DWORD dwPort
|
|
)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
PSCHEMALIST pTemp = NULL;
|
|
PSCHEMALIST pNewNode = NULL;
|
|
ADS_LDP * ld = NULL;
|
|
int nCount1 = 0, nCount2 = 0, nCount3 = 0;
|
|
LPWSTR *aValues1 = NULL, *aValues2 = NULL, *aValues3 = NULL;
|
|
LDAPMessage * res = NULL;
|
|
LDAPMessage *e = NULL;
|
|
LPWSTR aStrings[4]; // Attributes to fetch.
|
|
BOOL fBoundOk = FALSE; // have we at least once bound to
|
|
// domain successfully?
|
|
BOOL fNoData = FALSE;
|
|
|
|
ADsAssert(pRootDSE);
|
|
|
|
memset (pRootDSE, 0x00, sizeof(ROOTDSENODE));
|
|
|
|
ENTER_SUBSCHEMA_CRITSECT();
|
|
|
|
pTemp = gpSubSchemaList;
|
|
|
|
while (pTemp) {
|
|
|
|
if (EquivalentServers(pszLDAPServer, pTemp->pszLDAPServer)){
|
|
|
|
if (pTemp->fNoDataGot) {
|
|
//
|
|
// This is necessary for V2 server
|
|
// If BoundOk is not set we may end up not loading
|
|
// the default schema
|
|
//
|
|
fBoundOk = TRUE;
|
|
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
BAIL_ON_FAILURE(
|
|
hr = HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)
|
|
);
|
|
}
|
|
|
|
pRootDSE->fPagingSupported = pTemp->fPagingSupported;
|
|
pRootDSE->fSortingSupported = pTemp->fSortingSupported;
|
|
pRootDSE->fVLVSupported = pTemp->fVLVSupported;
|
|
pRootDSE->fAttribScopedSupported = pTemp->fAttribScopedSupported;
|
|
|
|
pRootDSE->dwSecDescType = pTemp->dwSecDescType;
|
|
pRootDSE->fDomScopeSupported = pTemp->fDomScopeSupported;
|
|
|
|
pRootDSE->fTalkingToAD = pTemp->fTalkingToAD;
|
|
pRootDSE->fTalkingToEnhancedAD = pTemp->fTalkingToEnhancedAD;
|
|
|
|
pRootDSE->fNoDataGot = pTemp->fNoDataGot;
|
|
|
|
pRootDSE->pszSubSchemaEntry = AllocADsStr(pTemp->pszSubSchemaEntry);
|
|
|
|
if (!pRootDSE->pszSubSchemaEntry) {
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
//
|
|
// we have at least once bound successfully to the domain
|
|
//
|
|
|
|
fBoundOk = TRUE;
|
|
|
|
goto error; // can't return direct, need clean up
|
|
}
|
|
|
|
pTemp = pTemp->pNext;
|
|
|
|
}
|
|
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
hr = LdapOpenObject(
|
|
pszLDAPServer,
|
|
NULL,
|
|
&ld,
|
|
Credentials,
|
|
dwPort
|
|
);
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
//
|
|
// we have once bound to the node successfully - just now
|
|
//
|
|
|
|
fBoundOk=TRUE;
|
|
|
|
//
|
|
// Ask only for the attributes we are intersted in.
|
|
//
|
|
aStrings[0] = LDAP_OPATT_SUBSCHEMA_SUBENTRY_W;
|
|
aStrings[1] = LDAP_OPATT_SUPPORTED_CONTROL_W;
|
|
aStrings[2] = LDAP_OPATT_SUPPORTED_CAPABILITIES_W;
|
|
aStrings[3] = NULL;
|
|
|
|
hr = LdapSearchS(
|
|
ld,
|
|
NULL,
|
|
LDAP_SCOPE_BASE,
|
|
L"(objectClass=*)",
|
|
aStrings,
|
|
0,
|
|
&res );
|
|
|
|
// Only one entry should be returned
|
|
|
|
if ( FAILED(hr)
|
|
|| FAILED(hr = LdapFirstEntry( ld, res, &e ))
|
|
)
|
|
{
|
|
goto error;
|
|
}
|
|
|
|
hr = LdapGetValues(
|
|
ld,
|
|
e,
|
|
LDAP_OPATT_SUBSCHEMA_SUBENTRY_W,
|
|
&aValues1,
|
|
&nCount1
|
|
);
|
|
|
|
if (SUCCEEDED(hr) && nCount1==0) {
|
|
//
|
|
// No data flag indicates that we read nothing but the
|
|
// search was a success.
|
|
//
|
|
fNoData = TRUE;
|
|
|
|
}
|
|
|
|
BAIL_ON_FAILURE(hr);
|
|
|
|
|
|
hr = LdapGetValues(
|
|
ld,
|
|
e,
|
|
LDAP_OPATT_SUPPORTED_CONTROL_W,
|
|
&aValues2,
|
|
&nCount2
|
|
);
|
|
|
|
//
|
|
// okay to have no values for supportedControl
|
|
//
|
|
if (FAILED(hr)) {
|
|
//
|
|
// Reset the error because we were really succesful
|
|
// in reading critical information.
|
|
//
|
|
hr = S_OK;
|
|
}
|
|
|
|
hr = LdapGetValues(
|
|
ld,
|
|
e,
|
|
LDAP_OPATT_SUPPORTED_CAPABILITIES_W,
|
|
&aValues3,
|
|
&nCount3
|
|
);
|
|
|
|
//
|
|
// okay to have no values for supportedControl
|
|
//
|
|
if (FAILED(hr)) {
|
|
//
|
|
// Reset the error because we were really succesful
|
|
// in reading critical information.
|
|
//
|
|
hr = S_OK;
|
|
}
|
|
|
|
ENTER_SUBSCHEMA_CRITSECT();
|
|
|
|
pTemp = gpSubSchemaList;
|
|
while (pTemp) {
|
|
|
|
if (EquivalentServers(pszLDAPServer, pTemp->pszLDAPServer)) {
|
|
//
|
|
// Found a match -looks like someone has come in before us
|
|
//
|
|
if (pTemp->fNoDataGot) {
|
|
//
|
|
// This is necessary for V2 server
|
|
//
|
|
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
BAIL_ON_FAILURE(
|
|
hr = HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE)
|
|
);
|
|
}
|
|
|
|
pRootDSE->fPagingSupported = pTemp->fPagingSupported;
|
|
pRootDSE->fSortingSupported = pTemp->fSortingSupported;
|
|
pRootDSE->fVLVSupported = pTemp->fVLVSupported;
|
|
pRootDSE->fAttribScopedSupported = pTemp->fAttribScopedSupported;
|
|
|
|
pRootDSE->dwSecDescType = pTemp->dwSecDescType;
|
|
pRootDSE->fDomScopeSupported = pTemp->fDomScopeSupported;
|
|
|
|
pRootDSE->fTalkingToAD = pTemp->fTalkingToAD;
|
|
pRootDSE->fTalkingToEnhancedAD = pTemp->fTalkingToEnhancedAD;
|
|
|
|
pRootDSE->fNoDataGot = pTemp->fNoDataGot;
|
|
|
|
pRootDSE->pszSubSchemaEntry = AllocADsStr(pTemp->pszSubSchemaEntry);
|
|
|
|
if (!pRootDSE->pszSubSchemaEntry) {
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
goto error; // clean up first before return
|
|
}
|
|
|
|
pTemp = pTemp->pNext;
|
|
|
|
}
|
|
|
|
pNewNode = (PSCHEMALIST)AllocADsMem(sizeof(SCHEMALIST));
|
|
|
|
if (!pNewNode) {
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
goto error; // clean up first before return
|
|
}
|
|
|
|
pNewNode->pNext = gpSubSchemaList;
|
|
|
|
pNewNode->pszLDAPServer = AllocADsStr(pszLDAPServer);
|
|
|
|
if (aValues1 && aValues1[0]) {
|
|
|
|
pNewNode->pszSubSchemaEntry = AllocADsStr(aValues1[0]);
|
|
pNewNode->fNoDataGot = FALSE;
|
|
}
|
|
else {
|
|
|
|
pNewNode->pszSubSchemaEntry = NULL;
|
|
pNewNode->fNoDataGot = TRUE;
|
|
}
|
|
|
|
//
|
|
// Default to this value
|
|
//
|
|
pNewNode->dwSecDescType = ADSI_LDAPC_SECDESC_NONE;
|
|
|
|
if (aValues2) {
|
|
|
|
for (int j=0; j<nCount2; j++) {
|
|
|
|
if (_wcsicmp(aValues2[j], LDAP_PAGED_RESULT_OID_STRING_W) == 0) {
|
|
pNewNode->fPagingSupported = TRUE;
|
|
}
|
|
else if (_wcsicmp(aValues2[j], LDAP_SERVER_SORT_OID_W) == 0) {
|
|
pNewNode->fSortingSupported = TRUE;
|
|
}
|
|
else if (_wcsicmp(aValues2[j], LDAP_SERVER_SD_FLAGS_OID_W) == 0) {
|
|
pNewNode->dwSecDescType = ADSI_LDAPC_SECDESC_NT;
|
|
}
|
|
else if (_wcsicmp(aValues2[j], ADSI_LDAP_OID_SECDESC_OLD) == 0) {
|
|
pNewNode->dwSecDescType = ADSI_LDAPC_SECDESC_OTHER;
|
|
}
|
|
else if (_wcsicmp(aValues2[j], LDAP_SERVER_DOMAIN_SCOPE_OID_W)
|
|
== 0) {
|
|
pNewNode->fDomScopeSupported = TRUE;
|
|
}
|
|
else if (_wcsicmp(aValues2[j], LDAP_CONTROL_VLVREQUEST_W) == 0) {
|
|
pNewNode->fVLVSupported = TRUE;
|
|
}
|
|
else if (_wcsicmp(aValues2[j], LDAP_SERVER_ASQ_OID_W) == 0) {
|
|
pNewNode->fAttribScopedSupported = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
pNewNode->fPagingSupported = FALSE;
|
|
pNewNode->fSortingSupported = FALSE;
|
|
pNewNode->fDomScopeSupported = FALSE;
|
|
pNewNode->fVLVSupported = FALSE;
|
|
pNewNode->fAttribScopedSupported = FALSE;
|
|
}
|
|
|
|
if (aValues3) {
|
|
for (int j=0; j<nCount3; j++) {
|
|
if (_wcsicmp(aValues3[j], LDAP_CAP_ACTIVE_DIRECTORY_OID_W)
|
|
== 0) {
|
|
pNewNode->fTalkingToAD = TRUE;
|
|
}
|
|
else if (_wcsicmp(aValues3[j],
|
|
LDAP_CAP_ACTIVE_DIRECTORY_V51_OID_W)
|
|
== 0) {
|
|
//
|
|
// Replace with correct OID from ntldap.h.
|
|
//
|
|
pNewNode->fTalkingToEnhancedAD = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
else {
|
|
//
|
|
// Should already be false but just in case.
|
|
//
|
|
pNewNode->fTalkingToAD = FALSE;
|
|
pNewNode->fTalkingToEnhancedAD = FALSE;
|
|
}
|
|
|
|
|
|
gpSubSchemaList = pNewNode;
|
|
|
|
if (fNoData == FALSE) {
|
|
|
|
pRootDSE->fPagingSupported = pNewNode->fPagingSupported;
|
|
pRootDSE->fSortingSupported = pNewNode->fSortingSupported;
|
|
pRootDSE->fVLVSupported = pNewNode->fVLVSupported;
|
|
pRootDSE->fAttribScopedSupported = pNewNode->fAttribScopedSupported;
|
|
|
|
pRootDSE->fNoDataGot = pNewNode->fNoDataGot;
|
|
pRootDSE->dwSecDescType = pNewNode->dwSecDescType;
|
|
pRootDSE->fDomScopeSupported = pNewNode->fDomScopeSupported;
|
|
pRootDSE->fTalkingToAD = pNewNode->fTalkingToAD;
|
|
pRootDSE->fTalkingToEnhancedAD = pNewNode->fTalkingToEnhancedAD;
|
|
|
|
pRootDSE->pszSubSchemaEntry = AllocADsStr(pNewNode->pszSubSchemaEntry);
|
|
|
|
if (!pRootDSE->pszSubSchemaEntry) {
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
|
|
LEAVE_SUBSCHEMA_CRITSECT();
|
|
|
|
error:
|
|
|
|
if (aValues1) {
|
|
|
|
LdapValueFree(aValues1);
|
|
}
|
|
|
|
if (aValues2) {
|
|
|
|
LdapValueFree(aValues2);
|
|
}
|
|
|
|
if (aValues3) {
|
|
LdapValueFree(aValues3);
|
|
}
|
|
|
|
if (res) {
|
|
|
|
LdapMsgFree(res);
|
|
}
|
|
|
|
if (ld) {
|
|
|
|
LdapCloseObject(ld);
|
|
}
|
|
|
|
//
|
|
// return to caller if we have at least once bound succsufully
|
|
// to the node
|
|
//
|
|
|
|
if (pfBoundOk)
|
|
*pfBoundOk = fBoundOk;
|
|
|
|
//
|
|
// Need to special case fNoData to ensure that the other code
|
|
// that relies on this eCode from this routine continues to
|
|
// work properly
|
|
//
|
|
if (fNoData) {
|
|
RRETURN(HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE));
|
|
} else
|
|
RRETURN(hr);
|
|
}
|
|
|
|
|