//--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1996 // // File: cschema.cxx // // Contents: LDAP // // // History: 09-01-96 yihsins Created. // //---------------------------------------------------------------------------- #include "ldap.hxx" #pragma hdrstop #define NT_SCHEMA_CLASS_NAME TEXT("classSchema") #define NT_SCHEMA_PROPERTY_NAME TEXT("attributeSchema") #define BEGIN_FILTER TEXT("(& (lDAPDisplayName=") #define END_FILTER TEXT(") (! (isdefunct=TRUE)))") struct _syntaxmapping { LPTSTR pszSyntax; LPTSTR pszOID; DWORD dwOMSyntax; } aSyntaxMap[] = { { TEXT("Boolean"), TEXT("2.5.5.8"), 1 }, { TEXT("Integer"), TEXT("2.5.5.9"), 2 }, { TEXT("OctetString"), TEXT("2.5.5.10"), 4 }, // The following are in ADS only { TEXT("Counter"), TEXT("2.5.5.9"), 2 }, { TEXT("ADsPath"), TEXT("2.5.5.12"), 64 }, { TEXT("EmailAddress"), TEXT("2.5.5.12"), 64 }, { TEXT("FaxNumber"), TEXT("2.5.5.12"), 64 }, { TEXT("Interval"), TEXT("2.5.5.9"), 2 }, { TEXT("List"), TEXT("2.5.5.10"), 4 }, { TEXT("NetAddress"), TEXT("2.5.5.12"), 64 }, { TEXT("Path"), TEXT("2.5.5.12"), 64 }, { TEXT("PhoneNumber"), TEXT("2.5.5.12"), 64 }, { TEXT("PostalAddress"), TEXT("2.5.5.12"), 64 }, { TEXT("SmallInterval"), TEXT("2.5.5.9"), 2 }, { TEXT("String"), TEXT("2.5.5.12"), 64 }, { TEXT("Time"), TEXT("2.5.5.11"), 23 }, // The following are in NTDS only { TEXT("INTEGER8"), TEXT("2.5.5.16"), 65 }, { TEXT("UTCTime"), TEXT("2.5.5.11"), 23 }, { TEXT("DN"), TEXT("2.5.5.1"), 127 }, { TEXT("OID"), TEXT("2.5.5.2"), 6 }, { TEXT("DirectoryString"), TEXT("2.5.5.12"), 64 }, { TEXT("PrintableString"), TEXT("2.5.5.5"), 19 }, { TEXT("CaseIgnoreString"), TEXT("2.5.5.4"), 20 }, { TEXT("NumericString"), TEXT("2.5.5.6"), 18 }, { TEXT("IA5String"), TEXT("2.5.5.5"), 22 }, { TEXT("PresentationAddresses"), TEXT("2.5.5.13"), 127 }, { TEXT("ORName"), TEXT("2.5.5.7"), 127 }, { TEXT("DNWithBinary"), TEXT("2.5.5.7"), 127}, // needs additional information to distinguish from ORName { TEXT("AccessPointDN"), TEXT("2.5.5.14"), 127 }, { TEXT("DNWithString"), TEXT("2.5.5.14"), 127 }, // needs additional information to distinguish from AccessPointDN { TEXT("NTSecurityDescriptor"), TEXT("2.5.5.15"), 66}, { TEXT("GeneralizedTime"), TEXT("2.5.5.11"), 24}, { TEXT("Enumeration"), TEXT("2.5.5.9"), 10 }, { TEXT("ReplicaLink"), TEXT("2.5.5.10"), 127 }, { TEXT("Sid"), TEXT("2.5.5.17"), 4 }, { TEXT("CaseExactString"), TEXT("2.5.5.3"), 27 } }; struct _classmapping { LPTSTR pszLdapClassName; LPTSTR pszADsClassName; const GUID *pCLSID; const GUID *pPrimaryInterfaceGUID; } aClassMap[] = { { TEXT("user"), USER_CLASS_NAME, &CLSID_LDAPUser, &IID_IADsUser }, // NTDS { TEXT("group"), GROUP_CLASS_NAME, &CLSID_LDAPGroup, &IID_IADsGroup }, // NTDS { TEXT("localGroup"), GROUP_CLASS_NAME, &CLSID_LDAPGroup, &IID_IADsGroup }, // NTDS // { TEXT("computer"), COMPUTER_CLASS_NAME, // &CLSID_LDAPComputer, &IID_IADsComputer }, // NTDS { TEXT("printQueue"), PRINTER_CLASS_NAME, &CLSID_LDAPPrintQueue, &IID_IADsPrintQueue }, // NTDS { TEXT("country"), TEXT("Country"), &CLSID_LDAPCountry, &IID_IADs }, { TEXT("locality"), TEXT("Locality"), &CLSID_LDAPLocality, &IID_IADsLocality }, { TEXT("organization"), TEXT("Organization"), &CLSID_LDAPO, &IID_IADsO }, { TEXT("organizationalUnit"), TEXT("Organizational Unit"), &CLSID_LDAPOU, &IID_IADsOU }, { TEXT("domain"), DOMAIN_CLASS_NAME, &CLSID_LDAPDomain, &IID_IADsDomain }, { TEXT("person"), USER_CLASS_NAME, &CLSID_LDAPUser, &IID_IADsUser }, { TEXT("organizationalPerson"), USER_CLASS_NAME, &CLSID_LDAPUser, &IID_IADsUser }, { TEXT("residentialPerson"), USER_CLASS_NAME, &CLSID_LDAPUser, &IID_IADsUser }, { TEXT("groupOfNames"), GROUP_CLASS_NAME, &CLSID_LDAPGroup, &IID_IADsGroup }, { TEXT("groupOfUniqueNames"), GROUP_CLASS_NAME, &CLSID_LDAPGroup, &IID_IADsGroup } // { TEXT("alias"), TEXT("Alias") }, // ..other classes in RFC 1788 new }; SYNTAXINFO g_aLDAPSyntax[] = { { TEXT("Boolean"), VT_BOOL }, { TEXT("Counter"), VT_I4 }, { TEXT("ADsPath"), VT_BSTR }, { TEXT("EmailAddress"), VT_BSTR }, { TEXT("FaxNumber"), VT_BSTR }, { TEXT("Integer"), VT_I4 }, { TEXT("Interval"), VT_I4 }, { TEXT("List"), VT_VARIANT }, // VT_BSTR | VT_ARRAY { TEXT("NetAddress"), VT_BSTR }, { TEXT("OctetString"), VT_VARIANT }, // VT_UI1| VT_ARRAY { TEXT("Path"), VT_BSTR }, { TEXT("PhoneNumber"), VT_BSTR }, { TEXT("PostalAddress"), VT_BSTR }, { TEXT("SmallInterval"), VT_I4 }, { TEXT("String"), VT_BSTR }, { TEXT("Time"), VT_DATE }, { TEXT("Integer8"), VT_DISPATCH }, { TEXT("UTCTime"), VT_DATE }, { TEXT("DN"), VT_BSTR }, { TEXT("OID"), VT_BSTR }, { TEXT("DirectoryString"), VT_BSTR }, { TEXT("PrintableString"), VT_BSTR }, { TEXT("CaseIgnoreString"), VT_BSTR }, { TEXT("NumericString"), VT_BSTR }, { TEXT("IA5String"), VT_BSTR }, { TEXT("PresentationAddresses"), VT_BSTR }, { TEXT("ORName"), VT_BSTR }, { TEXT("DNWithBinary"), VT_DISPATCH }, { TEXT("AccessPointDN"), VT_BSTR }, { TEXT("DNWithString"), VT_DISPATCH }, { TEXT("NTSecurityDescriptor"), VT_DISPATCH }, { TEXT("ObjectSecurityDescriptor"), VT_DISPATCH }, { TEXT("PresentationAddress"), VT_BSTR }, { TEXT("GeneralizedTime"), VT_DATE }, // // We do not support these // { TEXT("Enumeration")}, // { TEXT("ReplicaLink") }, // { TEXT("Sid") }, { TEXT("CaseExactString"), VT_BSTR} }; struct _OIDToNamemapping { LPTSTR pszAttributeTypeOID; LPTSTR pszFriendlyName; } aOIDtoNameMap[] = { { TEXT("1.2.840.113556.1.4.903"), TEXT("DNWithBinary") }, { TEXT("1.2.840.113556.1.4.904"), TEXT("DNWithString") }, // DnString also has the same OID as above { TEXT("1.2.840.113556.1.4.905"), TEXT("CaseIgnoreString") }, { TEXT("1.2.840.113556.1.4.906"), TEXT("INTEGER8") }, { TEXT("1.2.840.113556.1.4.907"), TEXT("ObjectSecurityDescriptor") }, // the type is ORName a type of string -> mapped to string. { TEXT("1.2.840.113556.1.4.1221"), TEXT("ORName") }, // the type is Undefined syntax in the server, so we are defaulting. { TEXT("1.2.840.113556.1.4.1222"), TEXT("Undefined") }, { TEXT("1.2.840.113556.1.4.1362"), TEXT("CaseExactString") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.10"), TEXT("CertificatePair") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.11"), TEXT("CountryString") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.12"), TEXT("DN") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.13"), TEXT("DataQualitySyntax") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.14"), TEXT("DeliveryMethod") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.15"), TEXT("DirectoryString") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.19"), TEXT("DSAQualitySyntax") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.2"), TEXT("AccessPointDN") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.21"), TEXT("EmhancedGuide") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.22"), TEXT("FacsimileTelephoneNumber") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.23"), TEXT("Fax") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.24"), TEXT("GeneralizedTime") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.25"), TEXT("Guide") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.26"), TEXT("IA5String") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.27"), TEXT("INTEGER") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.28"), TEXT("JPEG") },// not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.3"), TEXT("AttributeTypeDescription") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.32"), TEXT("MailPreference") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.33"), TEXT("ORAddress") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.34"), TEXT("NameAndOptionalUID") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.36"), TEXT("NumericString") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.37"), TEXT("ObjectClassDescription") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.38"), TEXT("OID") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.39"), TEXT("OtherMailBox") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.4"), TEXT("Audio") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.40"), TEXT("OctetString") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.41"), TEXT("PostalAddress") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.43"), TEXT("PresentationAddress") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.44"), TEXT("PrintableString") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.5"), TEXT("OctetString") }, // not in ours we map to Octet { TEXT("1.3.6.1.4.1.1466.115.121.1.50"), TEXT("TelephoneNumber") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.51"), TEXT("TeletexTerminalIdentifier") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.52"), TEXT("TelexNumber") }, // not in ours { TEXT("1.3.6.1.4.1.1466.115.121.1.53"), TEXT("UTCTime") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.6"), TEXT("BitString") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.7"), TEXT("Boolean") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.8"), TEXT("Certificate") }, { TEXT("1.3.6.1.4.1.1466.115.121.1.9"), TEXT("CertificateList") }, }; DWORD g_cLDAPSyntax = (sizeof(g_aLDAPSyntax)/sizeof(g_aLDAPSyntax[0])); typedef struct _classnamelist { LPTSTR pszClassName; _classnamelist *pNext; } CLASSNAME_LIST, *PCLASSNAME_LIST; BOOL GetSyntaxOID( LPTSTR pszSyntax, LPTSTR *ppszOID, DWORD *pdwOMSyntax ) { for ( int i = 0; i < ARRAY_SIZE(aSyntaxMap); i++ ) { if (_tcsicmp(pszSyntax, aSyntaxMap[i].pszSyntax) == 0 ) { *ppszOID = aSyntaxMap[i].pszOID; *pdwOMSyntax = aSyntaxMap[i].dwOMSyntax; return TRUE; } } *ppszOID = NULL; return FALSE; } BOOL GetFriendlyNameFromOID( LPTSTR pszOID, LPTSTR *ppszFriendlyName ) { HRESULT hr = S_OK; for ( int i = 0; i < ARRAY_SIZE(aOIDtoNameMap); i++ ) { if ( _tcsicmp( pszOID, aOIDtoNameMap[i].pszAttributeTypeOID ) == 0 ) { hr = ADsAllocString( aOIDtoNameMap[i].pszFriendlyName, ppszFriendlyName ); if (SUCCEEDED(hr)) return TRUE; else return FALSE; } } *ppszFriendlyName = NULL; return FALSE; } HRESULT MakeVariantFromStringArray( BSTR *bstrList, VARIANT *pvVariant ); HRESULT MakeVariantFromPropStringTable( int *propList, LDAP_SCHEMA_HANDLE hSchema, VARIANT *pvVariant ); HRESULT ConvertSafeArrayToVariantArray( VARIANT varSafeArray, PVARIANT *ppVarArray, PDWORD pdwNumVariants ); /* No Longer needed HRESULT DeleteSchemaEntry( LPTSTR pszADsPath, LPTSTR pszRelativeName, LPTSTR pszClassName, LPTSTR pszSubSchemaSubEntry, CCredentials& Credentials ); */ HRESULT BuildSchemaLDAPPathAndGetAttribute( IN LPTSTR pszParent, IN LPTSTR pszClass, IN LPTSTR pszSubSchemaSubEntry, IN BOOL fNew, IN CCredentials& Credentials, IN LPTSTR pszAttribs[], OUT LPWSTR * ppszSchemaLDAPServer, OUT LPWSTR * ppszSchemaLDAPDn, IN OUT PADS_LDP *pLd, OUT LDAPMessage **ppRes ); HRESULT BuildSchemaLDAPPath( LPTSTR pszParent, LPTSTR pszClass, LPTSTR pszSubSchemaSubEntry, LPWSTR * ppszSchemaLDAPServer, LPWSTR * ppszSchemaLDAPDn, BOOL fNew, ADS_LDP **pld, CCredentials& Credentials ); HRESULT MakePropArrayFromVariant( VARIANT vProp, SCHEMAINFO *hSchema, int **pOIDs, DWORD *pNumOfOids ); HRESULT MakePropArrayFromStringArray( LPTSTR *aValues, DWORD nCount, SCHEMAINFO *hSchema, int **pOIDs, DWORD *pNumOfOids ); HRESULT SchemaGetPrimaryInterface( LDAP_SCHEMA_HANDLE hSchema, LPTSTR pszClassName, GUID **ppPrimaryInterfaceGUID, GUID **ppCLSID ); STDMETHODIMP makeUnionVariantFromLdapObjects( LDAPOBJECTARRAY ldapSrcObjects1, LDAPOBJECTARRAY ldapSrcObjects2, VARIANT FAR * pvPossSuperiors ); STDMETHODIMP addStringIfAbsent( BSTR addString, BSTR *strArray, PDWORD dwArrIndx ); // // This functions puts the named string property into the cache // of the object as a CaseIgnoreString. It is meant for usage from // umi to put the simulated name property on schema objects. // HRESULT HelperPutStringPropertyInCache( LPWSTR pszName, LPWSTR pszStrProperty, CCredentials &Credentials, CPropertyCache *pPropCache ) { HRESULT hr = E_NOTIMPL; DWORD dwIndex = 0; VARIANT varBstr; LDAPOBJECTARRAY ldapDestObjects; LDAPOBJECTARRAY_INIT(ldapDestObjects); VariantInit(&varBstr); hr = ADsAllocString(pszStrProperty, &(varBstr.bstrVal)); BAIL_ON_FAILURE(hr); varBstr.vt = VT_BSTR; // // Conver the variant to LDAP objects we can cache. // hr = VarTypeToLdapTypeCopyConstruct( NULL, //ServerName not needed here, Credentials, LDAPTYPE_CASEIGNORESTRING, &varBstr, 1, &ldapDestObjects ); BAIL_ON_FAILURE(hr); // // Find this property in the cache // hr = pPropCache->findproperty( pszName, &dwIndex ); // // If this property does not exist in the // cache, add this property into the cache. // if (FAILED(hr)) { hr = pPropCache->addproperty( pszName ); BAIL_ON_FAILURE(hr); } // // Now update the property in the cache // hr = pPropCache->putproperty( pszName, PROPERTY_INIT, LDAPTYPE_CASEIGNORESTRING, ldapDestObjects ); BAIL_ON_FAILURE(hr); error: VariantClear(&varBstr); LdapTypeFreeLdapObjects( &ldapDestObjects ); RRETURN_EXP_IF_ERR(hr); } /******************************************************************/ /* Class CLDAPSchema /******************************************************************/ DEFINE_IDispatch_Implementation(CLDAPSchema) DEFINE_IADs_Implementation(CLDAPSchema) CLDAPSchema::CLDAPSchema() : _pDispMgr( NULL ), _pPropertyCache(NULL) { VariantInit( &_vFilter ); VariantInit( &_vHints ); _szServerPath[0] = 0; ENLIST_TRACKING(CLDAPSchema); } CLDAPSchema::~CLDAPSchema() { VariantClear( &_vFilter ); VariantClear( &_vHints ); delete _pDispMgr; delete _pPropertyCache; } HRESULT CLDAPSchema::CreateSchema( BSTR bstrParent, BSTR bstrName, LPTSTR pszServerPath, CCredentials& Credentials, DWORD dwObjectState, REFIID riid, void **ppvObj ) { CLDAPSchema FAR *pSchema = NULL; HRESULT hr = S_OK; OBJECTINFO ObjectInfo; POBJECTINFO pObjectInfo = &ObjectInfo; memset(pObjectInfo, 0, sizeof(OBJECTINFO)); hr = AllocateSchemaObject( &pSchema, Credentials ); BAIL_ON_FAILURE(hr); _tcscpy( pSchema->_szServerPath, pszServerPath ); hr = ADsObject(bstrParent, pObjectInfo); BAIL_ON_FAILURE(hr); pSchema->_dwPort = pObjectInfo->PortNumber; FreeObjectInfo(pObjectInfo); pObjectInfo = NULL; hr = pSchema->InitializeCoreObject( bstrParent, bstrName, SCHEMA_CLASS_NAME, CLSID_LDAPSchema, dwObjectState ); BAIL_ON_FAILURE(hr); // // See if we need to create the Umi object. // if (Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) { hr = ((CCoreADsObject*)pSchema)->InitUmiObject( IntfPropsSchema, pSchema->_pPropertyCache, (IADs*) pSchema, (IADs*) pSchema, riid, ppvObj, &(pSchema->_Credentials), pSchema->_dwPort ); BAIL_ON_FAILURE(hr); hr = HelperPutStringPropertyInCache( L"Name", bstrName, pSchema->_Credentials, pSchema->_pPropertyCache ); BAIL_ON_FAILURE(hr); RRETURN(S_OK); } hr = pSchema->QueryInterface( riid, ppvObj ); BAIL_ON_FAILURE(hr); pSchema->Release(); RRETURN(hr); error: FreeObjectInfo(pObjectInfo); *ppvObj = NULL; delete pSchema; RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::QueryInterface(REFIID iid, LPVOID FAR* ppv) { if (ppv == NULL) { RRETURN(E_POINTER); } if (IsEqualIID(iid, IID_IUnknown)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_IDispatch)) { *ppv = (IADs FAR *)this; } else if (IsEqualIID(iid, IID_IADs)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_IADsContainer)) { *ppv = (IADsContainer FAR *) this; } else if (IsEqualIID(iid, IID_ISupportErrorInfo)) { *ppv = (ISupportErrorInfo FAR *) this; } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } /* ISupportErrorInfo method */ STDMETHODIMP CLDAPSchema::InterfaceSupportsErrorInfo(THIS_ REFIID riid) { if (IsEqualIID(riid, IID_IADs) || IsEqualIID(riid, IID_IADsContainer)) { RRETURN(S_OK); } else { RRETURN(S_FALSE); } } /* IADs methods */ STDMETHODIMP CLDAPSchema::SetInfo(THIS) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::GetInfo(THIS) { HRESULT hr; hr = LDAPRefreshSchema(); RRETURN_EXP_IF_ERR(hr); } // // Helper function for Umi - defined in CCoreADsObject. // STDMETHODIMP CLDAPSchema::GetInfo(DWORD dwFlags) { if (dwFlags == GETINFO_FLAG_EXPLICIT) { RRETURN(GetInfo()); } // // All other cases we just say OK cause there is no way to go. // RRETURN(S_OK); } /* IADsContainer methods */ STDMETHODIMP CLDAPSchema::get_Count(long FAR* retval) { HRESULT hr; DWORD nNumOfClasses; DWORD nNumOfProperties; if ( !retval ) RRETURN(E_ADS_BAD_PARAMETER); hr = LdapGetSchemaObjectCount( _szServerPath, &nNumOfClasses, &nNumOfProperties, _Credentials, _dwPort ); if ( SUCCEEDED(hr)) *retval = nNumOfClasses + nNumOfProperties + g_cLDAPSyntax; RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::get_Filter(THIS_ VARIANT FAR* pVar) { if ( !pVar ) RRETURN(E_ADS_BAD_PARAMETER); HRESULT hr; VariantInit( pVar ); hr = VariantCopy( pVar, &_vFilter ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::put_Filter(THIS_ VARIANT Var) { HRESULT hr; hr = VariantCopy( &_vFilter, &Var ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::get_Hints(THIS_ VARIANT FAR* pVar) { if ( !pVar ) RRETURN(E_ADS_BAD_PARAMETER); HRESULT hr; VariantInit( pVar ); hr = VariantCopy( pVar, &_vHints ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::put_Hints(THIS_ VARIANT Var) { HRESULT hr; VariantClear(&_vHints); hr = VariantCopy( &_vHints, &Var ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::GetObject( THIS_ BSTR ClassName, BSTR RelativeName, IDispatch * FAR* ppObject) { DWORD dwBufferSize = 0; TCHAR *pszBuffer = NULL; HRESULT hr = S_OK; if (!RelativeName || !*RelativeName) { RRETURN(E_ADS_UNKNOWN_OBJECT); } dwBufferSize = ( _tcslen(_ADsPath) + _tcslen(RelativeName) + 2 ) * sizeof(TCHAR); // includes "/" pszBuffer = (LPTSTR) AllocADsMem( dwBufferSize ); if ( pszBuffer == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } _tcscpy(pszBuffer, _ADsPath); _tcscat(pszBuffer, TEXT("/")); _tcscat(pszBuffer, RelativeName); hr = ::GetObject( pszBuffer, _Credentials, (LPVOID *)ppObject ); BAIL_ON_FAILURE(hr); error: if ( pszBuffer ) FreeADsMem( pszBuffer ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::get__NewEnum(THIS_ IUnknown * FAR* retval) { HRESULT hr; IEnumVARIANT *penum = NULL; if ( !retval ) RRETURN(E_ADS_BAD_PARAMETER); *retval = NULL; // // Create new enumerator for items currently // in collection and QI for IUnknown // hr = CLDAPSchemaEnum::Create( (CLDAPSchemaEnum **)&penum, _ADsPath, _szServerPath, _vFilter, _Credentials ); BAIL_ON_FAILURE(hr); hr = penum->QueryInterface( IID_IUnknown, (VOID FAR* FAR*)retval ); BAIL_ON_FAILURE(hr); if ( penum ) penum->Release(); RRETURN(hr); error: if ( penum ) delete penum; RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::Create( THIS_ BSTR ClassName, BSTR RelativeName, IDispatch * FAR* ppObject) { HRESULT hr = S_OK; LDAP_SCHEMA_HANDLE hSchema = NULL; hr = SchemaOpen( _szServerPath, &hSchema, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); // // We can only create "Class","Property" here, "Syntax" is read-only // if ( ( _tcsicmp( ClassName, CLASS_CLASS_NAME ) == 0 ) || ( _tcsicmp( ClassName, NT_SCHEMA_CLASS_NAME ) == 0 ) ) { // // Now, create the class // hr = CLDAPClass::CreateClass( _ADsPath, hSchema, RelativeName, NULL, _Credentials, ADS_OBJECT_UNBOUND, IID_IUnknown, (void **) ppObject ); } else if ( ( _tcsicmp( ClassName, PROPERTY_CLASS_NAME ) == 0 ) || ( _tcsicmp( ClassName, NT_SCHEMA_PROPERTY_NAME ) == 0 ) ) { hr = CLDAPProperty::CreateProperty( _ADsPath, hSchema, RelativeName, NULL, _Credentials, ADS_OBJECT_UNBOUND, IID_IUnknown, (void **) ppObject ); } else { hr = E_ADS_BAD_PARAMETER; } error: if ( hSchema ) SchemaClose( &hSchema ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::Delete(THIS_ BSTR bstrClassName, BSTR bstrRelativeName ) { RRETURN(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::CopyHere(THIS_ BSTR SourceName, BSTR NewName, IDispatch * FAR* ppObject) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::MoveHere(THIS_ BSTR SourceName, BSTR NewName, IDispatch * FAR* ppObject) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::Get(THIS_ BSTR bstrName, VARIANT FAR* pvProp) { HRESULT hr = S_OK; LPTSTR pszSubSchemaSubEntry = NULL; LPTSTR pszSchemaRoot = NULL; // // For folks who know now what they do. // if (!pvProp) { BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } VariantInit( pvProp ); // Temporary hack if ( _tcsicmp( bstrName, TEXT("NTSchemaPath")) == 0 ) { hr = LdapGetSubSchemaSubEntryPath( _szServerPath, &pszSubSchemaSubEntry, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); if ( pszSubSchemaSubEntry == NULL ) // not NTDS server { hr = E_NOTIMPL; BAIL_ON_FAILURE(hr); } // the _tcschr is to get rid of "CN=Aggregate" pszSchemaRoot = _tcschr(pszSubSchemaSubEntry, TEXT(',')); if ( pszSchemaRoot == NULL ) // not NTDS server { hr = E_NOTIMPL; BAIL_ON_FAILURE(hr); } hr = ADsAllocString( pszSchemaRoot + 1, &(pvProp->bstrVal)); BAIL_ON_FAILURE(hr); pvProp->vt = VT_BSTR; } else { hr = E_NOTIMPL; } error: if ( pszSubSchemaSubEntry ) FreeADsStr( pszSubSchemaSubEntry ); if ( FAILED(hr)) VariantClear( pvProp ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSchema::Put(THIS_ BSTR bstrName, VARIANT vProp) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::GetEx(THIS_ BSTR bstrName, VARIANT FAR* pvProp) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::PutEx(THIS_ long lnControlCode, BSTR bstrName, VARIANT vProp) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSchema::GetInfoEx(THIS_ VARIANT vProperties, long lnReserved) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } HRESULT CLDAPSchema::AllocateSchemaObject( CLDAPSchema FAR * FAR * ppSchema, CCredentials& Credentials ) { CLDAPSchema FAR *pSchema = NULL; CAggregatorDispMgr FAR *pDispMgr = NULL; CPropertyCache FAR *pPropertyCache = NULL; HRESULT hr = S_OK; pSchema = new CLDAPSchema(); if ( pSchema == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); pDispMgr = new CAggregatorDispMgr(Credentials); if ( pDispMgr == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADs, (IADs *) pSchema, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADsContainer, (IADsContainer *) pSchema, DISPID_NEWENUM ); BAIL_ON_FAILURE(hr); hr = CPropertyCache::createpropertycache( (CCoreADsObject FAR *) pSchema, (IGetAttributeSyntax *) pSchema, &pPropertyCache ); BAIL_ON_FAILURE(hr); pSchema->_pPropertyCache = pPropertyCache; pSchema->_Credentials = Credentials; pSchema->_pDispMgr = pDispMgr; *ppSchema = pSchema; RRETURN(hr); error: delete pDispMgr; delete pSchema; delete pPropertyCache; RRETURN(hr); } HRESULT CLDAPSchema::LDAPRefreshSchema(THIS) { HRESULT hr = S_OK; // // Make the old schema obsolete. // We cannot delete the old schema since other objects might have // references to it. // hr = LdapMakeSchemaCacheObsolete( _szServerPath, _Credentials, _dwPort ); RRETURN_EXP_IF_ERR(hr); } // // Needed for dynamic dispid's in the property cache. // HRESULT CLDAPSchema::GetAttributeSyntax( LPWSTR szPropertyName, PDWORD pdwSyntaxId ) { HRESULT hr = S_OK; if ((_Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) && !_wcsicmp(L"Name", szPropertyName)) { *pdwSyntaxId = LDAPTYPE_CASEIGNORESTRING; } else { hr = E_ADS_PROPERTY_NOT_FOUND; } RRETURN_EXP_IF_ERR(hr); } /******************************************************************/ /* Class CLDAPClass /******************************************************************/ DEFINE_IDispatch_Implementation(CLDAPClass) DEFINE_IADs_Implementation(CLDAPClass) CLDAPClass::CLDAPClass() : _pDispMgr( NULL ), _pPropertyCache( NULL ), _bstrCLSID( NULL ), _bstrPrimaryInterface( NULL ), _bstrHelpFileName( NULL ), _lHelpFileContext( 0 ), _hSchema( NULL ), _pClassInfo( NULL ), _fNTDS( TRUE ), _pszLDAPServer(NULL), _pszLDAPDn(NULL), _ld( NULL ), _fLoadedInterfaceInfo(FALSE) { ENLIST_TRACKING(CLDAPClass); } CLDAPClass::~CLDAPClass() { delete _pDispMgr; delete _pPropertyCache; if ( _bstrCLSID ) { ADsFreeString( _bstrCLSID ); } if ( _bstrPrimaryInterface ) { ADsFreeString( _bstrPrimaryInterface ); } if ( _bstrHelpFileName ) { ADsFreeString( _bstrHelpFileName ); } if ( _hSchema ) { SchemaClose( &_hSchema ); _hSchema = NULL; } if ( _pszLDAPServer ) { FreeADsStr( _pszLDAPServer ); } if (_pszLDAPDn) { FreeADsStr(_pszLDAPDn); } if ( _ld ) { LdapCloseObject( _ld ); _ld = NULL; } } HRESULT CLDAPClass::CreateClass( BSTR bstrParent, LDAP_SCHEMA_HANDLE hSchema, BSTR bstrName, CLASSINFO *pClassInfo, CCredentials& Credentials, DWORD dwObjectState, REFIID riid, void **ppvObj ) { CLDAPClass FAR *pClass = NULL; HRESULT hr = S_OK; BOOL fUmiCall = FALSE; OBJECTINFO ObjectInfo; POBJECTINFO pObjectInfo = &ObjectInfo; VARIANT var; VariantInit(&var); memset(pObjectInfo, 0, sizeof(OBJECTINFO)); hr = AllocateClassObject(Credentials, &pClass ); BAIL_ON_FAILURE(hr); // // Some extra things need to be done if the call is from umi. // fUmiCall = Credentials.GetAuthFlags() & ADS_AUTH_RESERVED; pClass->_pClassInfo = pClassInfo; SchemaAddRef( hSchema ); pClass->_hSchema = hSchema; if ( pClassInfo ) // an existing class { if ( pClassInfo->pszHelpFileName ) { hr = ADsAllocString( pClassInfo->pszHelpFileName, &pClass->_bstrHelpFileName ); } pClass->_lHelpFileContext = pClassInfo->lHelpFileContext; hr = put_BSTR_Property( pClass, TEXT("governsID"), pClassInfo->pszOID); if ( SUCCEEDED(hr)) { hr = put_LONG_Property( pClass, TEXT("objectClassCategory"), pClassInfo->dwType ); BAIL_ON_FAILURE(hr); VariantInit( &var ); hr = MakeVariantFromPropStringTable( pClassInfo->pOIDsMustContain, pClass->_hSchema, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( pClass, TEXT("mustContain"), var ); BAIL_ON_FAILURE(hr); if (fUmiCall) { // // Need to add this to the cache as mandatoryProperties. // hr = put_VARIANT_Property( pClass, TEXT("mandatoryProperties"), var ); BAIL_ON_FAILURE(hr); // // We also need a dummy property called Name. // hr = put_BSTR_Property( pClass, TEXT("Name"), bstrName ); BAIL_ON_FAILURE(hr); } VariantClear( &var ); hr = MakeVariantFromPropStringTable( pClassInfo->pOIDsMayContain, pClass->_hSchema, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( pClass, TEXT("mayContain"), var ); BAIL_ON_FAILURE(hr); if (fUmiCall) { // // Again need to add as optionalProperties. // hr = put_VARIANT_Property( pClass, TEXT("optionalProperties"), var ); BAIL_ON_FAILURE(hr); } VariantClear( &var ); hr = MakeVariantFromStringArray( pClassInfo->pOIDsSuperiorClasses, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( pClass, TEXT("subClassOf"), var ); BAIL_ON_FAILURE(hr); VariantClear( &var ); hr = MakeVariantFromStringArray( pClassInfo->pOIDsAuxClasses, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( pClass, TEXT("auxiliaryClass"), var ); BAIL_ON_FAILURE(hr); VariantClear( &var ); pClass->_pPropertyCache->ClearAllPropertyFlags(); pClass->_fNTDS = TRUE; } else { pClass->_fNTDS = FALSE; } } hr = ADsObject(bstrParent, pObjectInfo); BAIL_ON_FAILURE(hr); pClass->_dwPort = pObjectInfo->PortNumber; FreeObjectInfo(pObjectInfo); pObjectInfo = NULL; hr = pClass->InitializeCoreObject( bstrParent, bstrName, CLASS_CLASS_NAME, CLSID_LDAPClass, dwObjectState ); BAIL_ON_FAILURE(hr); // // At this point update the info in the property cache // pClass->_pPropertyCache->SetObjInformation( &(pClass->_Credentials), pClass->_pszLDAPServer, pClass->_dwPort ); BAIL_ON_FAILURE(hr); // // If this is a umi call we need to return umi object. // if (fUmiCall) { hr = ((CCoreADsObject*)pClass)->InitUmiObject( IntfPropsSchema, pClass->_pPropertyCache, (IADs *) pClass, (IADs *) pClass, riid, ppvObj, &(pClass->_Credentials), pClass->_dwPort, pClass->_pszLDAPServer, pClass->_pszLDAPDn ); BAIL_ON_FAILURE(hr); RRETURN(S_OK); } hr = pClass->QueryInterface( riid, ppvObj ); BAIL_ON_FAILURE(hr); pClass->Release(); RRETURN(hr); error: *ppvObj = NULL; delete pClass; VariantClear(&var); FreeObjectInfo(pObjectInfo); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::QueryInterface(REFIID iid, LPVOID FAR* ppv) { if (ppv == NULL) { RRETURN(E_POINTER); } if (IsEqualIID(iid, IID_IUnknown)) { *ppv = (IADsClass FAR * ) this; } else if (IsEqualIID(iid, IID_IDispatch)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_ISupportErrorInfo)) { *ppv = (ISupportErrorInfo FAR *) this; } else if (IsEqualIID(iid, IID_IADs)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_IADsClass)) { *ppv = (IADsClass FAR *) this; } else if (IsEqualIID(iid, IID_IADsUmiHelperPrivate)) { *ppv = (IADsUmiHelperPrivate FAR *) this; } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } /* ISupportErrorInfo method */ STDMETHODIMP CLDAPClass::InterfaceSupportsErrorInfo(THIS_ REFIID riid) { if (IsEqualIID(riid, IID_IADs) || IsEqualIID(riid, IID_IADsClass)) { RRETURN(S_OK); } else { RRETURN(S_FALSE); } } /* IADs methods */ STDMETHODIMP CLDAPClass::SetInfo(THIS) { HRESULT hr = S_OK; BOOL fChanged = FALSE; if ( !_fNTDS ) RRETURN(E_NOTIMPL); if ( GetObjectState() == ADS_OBJECT_UNBOUND ) { hr = LDAPCreateObject(); BAIL_ON_FAILURE(hr); fChanged = TRUE; // // If the create succeded, set the object type to bound // SetObjectState(ADS_OBJECT_BOUND); } else { hr = LDAPSetObject( &fChanged ); BAIL_ON_FAILURE(hr); } // // Need to refresh the schema // if ( SUCCEEDED(hr) && fChanged ) hr = LDAPRefreshSchema(); error: RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPClass::LDAPSetObject( BOOL *pfChanged ) { HRESULT hr = S_OK; BOOL fUpdated = FALSE; BOOL fUpdateMustContain = FALSE; BOOL fUpdateMayContain = FALSE; VARIANT var; int *pOIDs = NULL; DWORD nNumOfOids = 0; LDAPModW **aMod = NULL; DWORD dwNumOfMods = 0; DWORD dwNumOfModsNeedFreeing = 0; *pfChanged = FALSE; hr = _pPropertyCache->IsPropertyUpdated( TEXT("mustContain"), &fUpdated); BAIL_ON_FAILURE(hr); if ( fUpdated ) { VariantInit(&var); hr = get_VARIANT_Property( this, TEXT("mustContain"), &var ); BAIL_ON_FAILURE(hr); hr = MakePropArrayFromVariant( var, (SCHEMAINFO *) _hSchema, &pOIDs, &nNumOfOids ); BAIL_ON_FAILURE(hr); hr = FindModifications( pOIDs, nNumOfOids, TEXT("mustContain"), &aMod, &dwNumOfMods ); BAIL_ON_FAILURE(hr); // This flag needs to be cleared temporarily so that // LDAPMarshallProperties2 below will not try to update // this property. We will reset the flag right later. hr = _pPropertyCache->ClearPropertyFlag( TEXT("mustContain")); BAIL_ON_FAILURE(hr); fUpdateMustContain = TRUE; VariantClear(&var); FreeADsMem( pOIDs ); pOIDs = NULL; } hr = _pPropertyCache->IsPropertyUpdated( TEXT("mayContain"), &fUpdated); BAIL_ON_FAILURE(hr); if ( fUpdated ) { VariantInit(&var); hr = get_VARIANT_Property( this, TEXT("mayContain"), &var ); BAIL_ON_FAILURE(hr); hr = MakePropArrayFromVariant( var, (SCHEMAINFO *) _hSchema, &pOIDs, &nNumOfOids ); BAIL_ON_FAILURE(hr); hr = FindModifications( pOIDs, nNumOfOids, TEXT("mayContain"), &aMod, &dwNumOfMods ); BAIL_ON_FAILURE(hr); // This flag needs to be cleared temporarily so that // LDAPMarshallProperties2 below will not try to update // this property. We will reset the flag later on. hr = _pPropertyCache->ClearPropertyFlag( TEXT("mayContain")); BAIL_ON_FAILURE(hr); fUpdateMayContain = TRUE; VariantClear(&var); FreeADsMem( pOIDs ); pOIDs = NULL; } dwNumOfModsNeedFreeing = dwNumOfMods; hr = _pPropertyCache->LDAPMarshallProperties2( &aMod, &dwNumOfMods ); BAIL_ON_FAILURE(hr); if ( aMod == NULL ) // There are no changes that needs to be modified RRETURN(S_OK); // // Reset the flags so that if LdapModifyS fails, they are still flagged // as needed to be updated. // if ( fUpdateMustContain ) { hr = _pPropertyCache->SetPropertyFlag( TEXT("mustContain"), PROPERTY_UPDATE ); BAIL_ON_FAILURE(hr); } if ( fUpdateMayContain ) { hr = _pPropertyCache->SetPropertyFlag( TEXT("mayContain"), PROPERTY_UPDATE ); BAIL_ON_FAILURE(hr); } // // Send the request to the server // if (_pszLDAPDn == NULL ) { hr = BuildSchemaLDAPPath( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, &_pszLDAPServer, &_pszLDAPDn, _pClassInfo == NULL, &_ld, _Credentials ); BAIL_ON_FAILURE(hr); } hr = LdapModifyS( _ld, _pszLDAPDn, aMod ); BAIL_ON_FAILURE(hr); // We are successful at this point, // So, clean up the flags in the cache so the same operation // won't be repeated on the next SetInfo() _pPropertyCache->ClearAllPropertyFlags(); *pfChanged = TRUE; error: VariantClear(&var); if ( pOIDs ) FreeADsMem( pOIDs ); if (aMod) { for ( DWORD i = 0; i < dwNumOfModsNeedFreeing; i++ ) { FreeADsMem((*aMod)[i].mod_values); } if ( *aMod ) FreeADsMem( *aMod ); FreeADsMem( aMod ); } RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPClass::LDAPCreateObject() { HRESULT hr = S_OK; LDAPModW **aMod = NULL; DWORD dwIndex = 0; VARIANT v; BOOL fNTSecDes = FALSE; SECURITY_INFORMATION NewSeInfo; // // Get the LDAP path of the schema entry // if (_pszLDAPDn == NULL ) { hr = BuildSchemaLDAPPath( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, &_pszLDAPServer, &_pszLDAPDn, _pClassInfo == NULL, &_ld, _Credentials ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("objectClass"), &dwIndex ) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_BSTR; V_BSTR(&v) = NT_SCHEMA_CLASS_NAME; hr = Put( TEXT("objectClass"), v ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("cn"), &dwIndex ) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_BSTR; V_BSTR(&v) = _Name; hr = Put( TEXT("cn"), v ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("lDAPDisplayName"), &dwIndex ) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_BSTR; V_BSTR(&v) = _Name; hr = Put( TEXT("lDAPDisplayName"), v ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("objectClassCategory"), &dwIndex) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_I4; V_I4(&v) = CLASS_TYPE_STRUCTURAL; hr = Put( TEXT("objectClassCategory"), v ); BAIL_ON_FAILURE(hr); } hr = _pPropertyCache->LDAPMarshallProperties( &aMod, &fNTSecDes, &NewSeInfo ); BAIL_ON_FAILURE(hr); if ( _ld == NULL ) { hr = LdapOpenObject( _pszLDAPServer, _pszLDAPDn, &_ld, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); } hr = LdapAddS( _ld, _pszLDAPDn, aMod ); BAIL_ON_FAILURE(hr); // We are successful at this point, // So, clean up the flags in the cache so the same operation // won't be repeated on the next SetInfo() _pPropertyCache->ClearAllPropertyFlags(); error: if (aMod) { if ( *aMod ) FreeADsMem( *aMod ); FreeADsMem( aMod ); } RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPClass::LDAPRefreshSchema(THIS) { HRESULT hr = S_OK; TCHAR *pszLDAPServer = NULL; TCHAR *pszLDAPDn = NULL; DWORD dwPort = 0; // // Get the server name // hr = BuildLDAPPathFromADsPath2( _Parent, &pszLDAPServer, &pszLDAPDn, &dwPort ); BAIL_ON_FAILURE(hr); // // Make the old schema obsolete so we will get the new schema next // time we asked for it. // We cannot delete the old schema since other objects might have // references to it. // hr = LdapMakeSchemaCacheObsolete( pszLDAPServer, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); error: if (pszLDAPServer) { FreeADsStr(pszLDAPServer); } if (pszLDAPDn) { FreeADsStr(pszLDAPDn); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::GetInfo(THIS) { HRESULT hr = S_OK; VARIANT var; BOOL fUmiCall = FALSE; LPWSTR pszLDAPServer = NULL; LPWSTR pszLDAPDn = NULL; DWORD dwPort = 0; VariantInit(&var); if ( GetObjectState() == ADS_OBJECT_UNBOUND ) RRETURN(E_ADS_OBJECT_UNBOUND); // // Update the umicall flag - we need to add items to prop cache // if the call is from Umi. // fUmiCall = _Credentials.GetAuthFlags() & ADS_AUTH_RESERVED; // // Get the server name // hr = BuildLDAPPathFromADsPath2( _Parent, &pszLDAPServer, &pszLDAPDn, &dwPort ); BAIL_ON_FAILURE(hr); // // AjayR - 04-05-99 do not understand why this is done // I do not think you need to obsolete the cache on the // GetInfo for a class - for the schema container yes. // hr = LdapMakeSchemaCacheObsolete( pszLDAPServer, _Credentials, dwPort ); BAIL_ON_FAILURE(hr); // // Release the original schema info // SchemaClose( &_hSchema ); // // Get the new schema info // hr = SchemaOpen( pszLDAPServer, &_hSchema, _Credentials, _dwPort // IsGCNamespace(_Parent) ); BAIL_ON_FAILURE(hr); // // Find the new class info // hr = SchemaGetClassInfo( _hSchema, _Name, &_pClassInfo ); BAIL_ON_FAILURE( hr ); if ( _pClassInfo == NULL ) { // Class name not found, set error hr = E_ADS_BAD_PATHNAME; BAIL_ON_FAILURE(hr); } _pPropertyCache->flushpropertycache(); hr = put_BSTR_Property( this, TEXT("governsID"), _pClassInfo->pszOID); BAIL_ON_FAILURE(hr); hr = put_LONG_Property( this, TEXT("objectClassCategory"), _pClassInfo->dwType ); BAIL_ON_FAILURE(hr); hr = MakeVariantFromPropStringTable( _pClassInfo->pOIDsMustContain, _hSchema, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( this, TEXT("mustContain"), var ); BAIL_ON_FAILURE(hr); if (fUmiCall) { // // Add as mandatoryProperties to cache. // hr = put_VARIANT_Property( this, TEXT("mandatoryProperties"), var ); BAIL_ON_FAILURE(hr); hr = put_BSTR_Property( this, TEXT("Name"), this->_Name); BAIL_ON_FAILURE(hr); } VariantClear( &var ); hr = MakeVariantFromPropStringTable( _pClassInfo->pOIDsMayContain, _hSchema, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( this, TEXT("mayContain"), var ); BAIL_ON_FAILURE(hr); if (fUmiCall) { // // Add to the cache as optionalProperties. // hr = put_VARIANT_Property( this, TEXT("optionalProperties"), var ); BAIL_ON_FAILURE(hr); } VariantClear( &var ); hr = MakeVariantFromStringArray( _pClassInfo->pOIDsSuperiorClasses, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( this, TEXT("subClassOf"), var ); BAIL_ON_FAILURE(hr); VariantClear( &var ); hr = MakeVariantFromStringArray( _pClassInfo->pOIDsAuxClasses, &var ); BAIL_ON_FAILURE(hr); hr = put_VARIANT_Property( this, TEXT("auxiliaryClass"), var ); BAIL_ON_FAILURE(hr); VariantClear( &var ); if (_fNTDS) { // // Read the extra NTDS specific schema properties. // hr = GetNTDSSchemaInfo(TRUE); BAIL_ON_FAILURE(hr); } _pPropertyCache->ClearAllPropertyFlags(); _pPropertyCache->setGetInfoFlag(); error: if (pszLDAPServer) { FreeADsStr(pszLDAPServer); } if (pszLDAPDn) { FreeADsStr(pszLDAPDn); } VariantClear(&var); RRETURN_EXP_IF_ERR(hr); // All current information are in _pClassInfo } // // This routine is called only when the server is AD. // HRESULT CLDAPClass::GetNTDSSchemaInfo( BOOL fForce ) { HRESULT hr = S_OK; LPTSTR aStrings[] = { TEXT("cn"), TEXT("displaySpecification"), TEXT("schemaIDGUID"), TEXT("possibleInferiors"), TEXT("rDNAttid"), TEXT("possSuperiors"), TEXT("systemPossSuperiors"), NULL }; LDAPMessage *res = NULL; if (_pszLDAPDn == NULL) { // // Need to get the dn for this object and also // the attributes we are interested in. // hr = BuildSchemaLDAPPathAndGetAttribute( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, _pClassInfo == NULL, _Credentials, aStrings, &_pszLDAPServer, &_pszLDAPDn, &_ld, &res ); } else { // // Looks like we just need the attributes in this case. // hr = LdapSearchS( _ld, _pszLDAPDn, LDAP_SCOPE_BASE, L"(objectClass=*)", aStrings, FALSE, &res ); } BAIL_ON_FAILURE(hr); // // If we succeeded we should unmarshall properties into the cache. // hr = _pPropertyCache->LDAPUnMarshallProperties( _pszLDAPServer, _ld, res, fForce, _Credentials ); BAIL_ON_FAILURE(hr); _pPropertyCache->setGetInfoFlag(); error: if (res) { LdapMsgFree(res); } RRETURN(hr); } // // Helper function for Umi - defined in CCoreADsObject. // STDMETHODIMP CLDAPClass::GetInfo(DWORD dwFlags) { HRESULT hr = S_OK; if (dwFlags == GETINFO_FLAG_EXPLICIT) { RRETURN(GetInfo()); } else { // // Read NTDS info if this is not an implicit as needed call. // That is this just a regular implicit GetInfo. // if (_fNTDS && dwFlags != GETINFO_FLAG_IMPLICIT_AS_NEEDED ) { // // Read the extra NTDS specific schema properties. // hr = GetNTDSSchemaInfo(FALSE); } } RRETURN(hr); } STDMETHODIMP CLDAPClass::Get(THIS_ BSTR bstrName, VARIANT FAR* pvProp) { HRESULT hr = S_OK; DWORD dwSyntaxId; LDAPOBJECTARRAY ldapSrcObjects; DWORD dwStatus = 0; LDAPOBJECTARRAY_INIT(ldapSrcObjects); // // For folks who know now what they do. // if (!pvProp) { BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } // // retrieve data object from cache; if one exists // if ( GetObjectState() == ADS_OBJECT_UNBOUND ) { hr = _pPropertyCache->unboundgetproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_FAIL; } } else { hr = _pPropertyCache->getproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_ADS_PROPERTY_NOT_FOUND; } } BAIL_ON_FAILURE(hr); // // translate the Ldap objects to variants // if ( ldapSrcObjects.dwCount == 1 ) { hr = LdapTypeToVarTypeCopy( _pszLDAPServer, _Credentials, ldapSrcObjects.pLdapObjects, dwSyntaxId, pvProp ); } else { hr = LdapTypeToVarTypeCopyConstruct( _pszLDAPServer, _Credentials, ldapSrcObjects, dwSyntaxId, pvProp ); } BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapSrcObjects ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::Put(THIS_ BSTR bstrName, VARIANT vProp) { HRESULT hr = S_OK; DWORD dwSyntaxId = 0; DWORD dwIndex = 0; LDAPOBJECTARRAY ldapDestObjects; DWORD dwNumValues = 0; VARIANT * pVarArray = NULL; VARIANT * pvProp = NULL; VARIANT vDefProp; VariantInit(&vDefProp); LDAPOBJECTARRAY_INIT(ldapDestObjects); hr = SchemaGetSyntaxOfAttribute( _hSchema, bstrName, &dwSyntaxId ); if (FAILED(hr)) { // // Need to see if this is either mandatoryProperties or // OptionalProperties that we special case for Umi Objects. // if (!_wcsicmp(L"mandatoryProperties", bstrName) || !_wcsicmp(L"optionalProperties", bstrName) ) { dwSyntaxId = LDAPTYPE_CASEIGNORESTRING; hr = S_OK; } } BAIL_ON_FAILURE(hr); if ( dwSyntaxId == LDAPTYPE_UNKNOWN ) { hr = E_ADS_CANT_CONVERT_DATATYPE; BAIL_ON_FAILURE(hr); } // // A VT_BYREF|VT_VARIANT may expand to a VT_VARIANT|VT_ARRAY. // We should dereference a VT_BYREF|VT_VARIANT once and see // what's inside. // pvProp = &vProp; if (V_VT(pvProp) == (VT_BYREF|VT_VARIANT)) { pvProp = V_VARIANTREF(&vProp); } if ((V_VT(pvProp) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) || (V_VT(pvProp) == (VT_VARIANT|VT_ARRAY))) { hr = ConvertSafeArrayToVariantArray( *pvProp, &pVarArray, &dwNumValues ); // returns E_FAIL if *pvProp is invalid if (hr == E_FAIL) hr = E_ADS_BAD_PARAMETER; BAIL_ON_FAILURE(hr); pvProp = pVarArray; } else { // // If pvProp is a reference to a fundamental type, we // have to derefernce it once. // if (V_ISBYREF(pvProp)) { hr = VariantCopyInd(&vDefProp, pvProp); BAIL_ON_FAILURE(hr); pvProp = &vDefProp; } dwNumValues = 1; } #if 0 // // check if this is a legal property for this object, // hr = ValidatePropertyinCache( szLDAPTreeName, _ADsClass, bstrName, &dwSyntaxId ); BAIL_ON_FAILURE(hr); #endif // // check if the variant maps to the syntax of this property // if ( dwNumValues > 0 ) { hr = VarTypeToLdapTypeCopyConstruct( _pszLDAPServer, _Credentials, dwSyntaxId, pvProp, dwNumValues, &ldapDestObjects ); BAIL_ON_FAILURE(hr); } // // Find this property in the cache // hr = _pPropertyCache->findproperty( bstrName, &dwIndex ); // // If this property does not exist in the // cache, add this property into the cache. // if (FAILED(hr)) { hr = _pPropertyCache->addproperty( bstrName ); BAIL_ON_FAILURE(hr); } // // Now update the property in the cache // hr = _pPropertyCache->putproperty( bstrName, PROPERTY_UPDATE, dwSyntaxId, ldapDestObjects ); BAIL_ON_FAILURE(hr); error: VariantClear(&vDefProp); LdapTypeFreeLdapObjects( &ldapDestObjects ); if (pVarArray) { DWORD i = 0; for (i = 0; i < dwNumValues; i++) { VariantClear(pVarArray + i); } FreeADsMem(pVarArray); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::GetEx(THIS_ BSTR bstrName, VARIANT FAR* pvProp) { HRESULT hr = S_OK; DWORD dwSyntaxId; LDAPOBJECTARRAY ldapSrcObjects; DWORD dwStatus = 0; LDAPOBJECTARRAY_INIT(ldapSrcObjects); // // For those who know no not what they do // if (!pvProp) { BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } // // retrieve data object from cache; if one exists // if ( GetObjectState() == ADS_OBJECT_UNBOUND ) { hr = _pPropertyCache->unboundgetproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_FAIL; } } else { hr = _pPropertyCache->getproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_ADS_PROPERTY_NOT_FOUND; } } BAIL_ON_FAILURE(hr); // // translate the Ldap objects to variants // hr = LdapTypeToVarTypeCopyConstruct( _pszLDAPServer, _Credentials, ldapSrcObjects, dwSyntaxId, pvProp ); BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapSrcObjects ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::PutEx(THIS_ long lnControlCode, BSTR bstrName, VARIANT vProp) { HRESULT hr = S_OK; DWORD dwSyntaxId = 0; DWORD dwFlags = 0; DWORD dwIndex = 0; LDAPOBJECTARRAY ldapDestObjects; DWORD dwNumValues = 0; VARIANT * pVarArray = NULL; VARIANT * pvProp = NULL; LDAPOBJECTARRAY_INIT(ldapDestObjects); switch (lnControlCode) { case ADS_PROPERTY_CLEAR: dwFlags = PROPERTY_DELETE; break; case ADS_PROPERTY_UPDATE: dwFlags = PROPERTY_UPDATE; break; default: RRETURN(hr = E_ADS_BAD_PARAMETER); } hr = SchemaGetSyntaxOfAttribute( _hSchema, bstrName, &dwSyntaxId ); BAIL_ON_FAILURE(hr); if ( dwSyntaxId == LDAPTYPE_UNKNOWN ) { hr = E_ADS_CANT_CONVERT_DATATYPE; BAIL_ON_FAILURE(hr); } #if 0 // // check if this is a legal property for this object, // hr = ValidatePropertyinCache( szLDAPTreeName, _ADsClass, bstrName, &dwSyntaxId ); BAIL_ON_FAILURE(hr); #endif if ( dwFlags != PROPERTY_DELETE ) { // // A VT_BYREF|VT_VARIANT may expand to a VT_VARIANT|VT_ARRAY. // We should dereference a VT_BYREF|VT_VARIANT once and see // what's inside. // pvProp = &vProp; if (V_VT(pvProp) == (VT_BYREF|VT_VARIANT)) { pvProp = V_VARIANTREF(&vProp); } if ((V_VT(pvProp) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) || (V_VT(pvProp) == (VT_VARIANT|VT_ARRAY))) { hr = ConvertSafeArrayToVariantArray( *pvProp, &pVarArray, &dwNumValues ); // returns E_FAIL if *pvProp is invalid if (hr == E_FAIL) hr = E_ADS_BAD_PARAMETER; BAIL_ON_FAILURE(hr); pvProp = pVarArray; } else { hr = E_FAIL; BAIL_ON_FAILURE(hr); } // // check if the variant maps to the syntax of this property // if ( dwNumValues > 0 ) { hr = VarTypeToLdapTypeCopyConstruct( _pszLDAPServer, _Credentials, dwSyntaxId, pvProp, dwNumValues, &ldapDestObjects ); BAIL_ON_FAILURE(hr); } } // // Find this property in the cache // hr = _pPropertyCache->findproperty( bstrName, &dwIndex ); // // If this property does not exist in the // cache, add this property into the cache. // if (FAILED(hr)) { hr = _pPropertyCache->addproperty( bstrName ); BAIL_ON_FAILURE(hr); } // // Now update the property in the cache // hr = _pPropertyCache->putproperty( bstrName, PROPERTY_UPDATE, dwSyntaxId, ldapDestObjects ); BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapDestObjects ); if (pVarArray) { DWORD i = 0; for (i = 0; i < dwNumValues; i++) { VariantClear(pVarArray + i); } FreeADsMem(pVarArray); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::GetInfoEx(THIS_ VARIANT vProperties, long lnReserved) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } /* IADsContainer methods */ /* IADsClass methods */ HRESULT CLDAPClass::LoadInterfaceInfo(void) { HRESULT hr = S_OK; BSTR bstrTmp = NULL; if ( _pClassInfo ) { GUID *pPrimaryInterfaceGUID = NULL; GUID *pCLSID = NULL; hr = SchemaGetPrimaryInterface( _hSchema, _Name, &pPrimaryInterfaceGUID, &pCLSID ); if ( pPrimaryInterfaceGUID == NULL ) pPrimaryInterfaceGUID = (GUID *) &IID_IADs; // // Set the primary interface string // hr = StringFromCLSID((REFCLSID)*(pPrimaryInterfaceGUID), &bstrTmp ); BAIL_ON_FAILURE(hr); hr = ADsAllocString( bstrTmp, &_bstrPrimaryInterface); BAIL_ON_FAILURE(hr); CoTaskMemFree(bstrTmp); bstrTmp = NULL; if ( pCLSID ) { // // Set the CLSID string // hr = StringFromCLSID( (REFCLSID) *(pCLSID), &bstrTmp ); BAIL_ON_FAILURE(hr); hr = ADsAllocString( bstrTmp, &_bstrCLSID ); BAIL_ON_FAILURE(hr); CoTaskMemFree(bstrTmp); bstrTmp = NULL; } } error: if ( bstrTmp != NULL ) CoTaskMemFree(bstrTmp); RRETURN(hr); } STDMETHODIMP CLDAPClass::get_PrimaryInterface( THIS_ BSTR FAR *pbstrGUID ) { HRESULT hr; if ( !pbstrGUID ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if (!_fLoadedInterfaceInfo) { hr = LoadInterfaceInfo(); BAIL_ON_FAILURE(hr); _fLoadedInterfaceInfo = TRUE; } hr = ADsAllocString( _bstrPrimaryInterface? _bstrPrimaryInterface : TEXT(""), pbstrGUID ); error: RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_CLSID( THIS_ BSTR FAR *pbstrCLSID ) { HRESULT hr; if ( !pbstrCLSID ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if (!_fLoadedInterfaceInfo) { hr = LoadInterfaceInfo(); BAIL_ON_FAILURE(hr); _fLoadedInterfaceInfo = TRUE; } hr = ADsAllocString( _bstrCLSID? _bstrCLSID: TEXT(""), pbstrCLSID ); error: RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_CLSID( THIS_ BSTR bstrCLSID ) { RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPClass::get_OID( THIS_ BSTR FAR *retval ) { if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); HRESULT hr; if ( _fNTDS ) { GET_PROPERTY_BSTR( this, governsID ); } else if ( _pClassInfo ) { hr = ADsAllocString( _pClassInfo->pszOID? _pClassInfo->pszOID : TEXT(""), retval ); RRETURN_EXP_IF_ERR(hr); } else { hr = ADsAllocString( TEXT(""), retval ); RRETURN_EXP_IF_ERR(hr); } } STDMETHODIMP CLDAPClass::put_OID( THIS_ BSTR bstrOID ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR(E_NOTIMPL); HRESULT hr = put_BSTR_Property( this, TEXT("governsID"), bstrOID ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_Abstract( THIS_ VARIANT_BOOL FAR *pfAbstract ) { if ( !pfAbstract ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); HRESULT hr = S_OK; long lClassType = CLASS_TYPE_STRUCTURAL; // by default if ( _fNTDS ) { hr = get_LONG_Property( this, TEXT("objectClassCategory"), &lClassType ); BAIL_ON_FAILURE(hr); } else if ( _pClassInfo ) { lClassType = _pClassInfo->dwType; } *pfAbstract = lClassType == CLASS_TYPE_ABSTRACT ? VARIANT_TRUE : VARIANT_FALSE; error: RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_Abstract( THIS_ VARIANT_BOOL fAbstract ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); long lClassType; HRESULT hr = get_LONG_Property( this, TEXT("objectClassCategory"), &lClassType ); if ( SUCCEEDED(hr)) { if ( ( fAbstract && lClassType == CLASS_TYPE_ABSTRACT ) || ( !fAbstract && lClassType != CLASS_TYPE_ABSTRACT ) ) { RRETURN(S_OK); // Nothing to set } } hr = put_LONG_Property( (IADs *) this, TEXT("objectClassCategory"), fAbstract? CLASS_TYPE_ABSTRACT : CLASS_TYPE_STRUCTURAL ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_Auxiliary( THIS_ VARIANT_BOOL FAR *pfAuxiliary ) { if ( !pfAuxiliary ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); HRESULT hr = S_OK; long lClassType = CLASS_TYPE_STRUCTURAL; // by default if ( _fNTDS ) { hr = get_LONG_Property( this, TEXT("objectClassCategory"), &lClassType ); BAIL_ON_FAILURE(hr); } else if ( _pClassInfo ) { lClassType = _pClassInfo->dwType; } *pfAuxiliary = lClassType == CLASS_TYPE_AUXILIARY ? VARIANT_TRUE : VARIANT_FALSE; error: RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_Auxiliary( THIS_ VARIANT_BOOL fAuxiliary ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); long lClassType = CLASS_TYPE_STRUCTURAL; HRESULT hr = get_LONG_Property( this, TEXT("objectClassCategory"), &lClassType ); if ( SUCCEEDED(hr)) { if ( ( fAuxiliary && lClassType == CLASS_TYPE_AUXILIARY ) || ( !fAuxiliary && lClassType != CLASS_TYPE_AUXILIARY ) ) { RRETURN(S_OK); // Nothing to set } } hr = put_LONG_Property( (IADs *) this, TEXT("objectClassCategory"), fAuxiliary? CLASS_TYPE_AUXILIARY : CLASS_TYPE_STRUCTURAL ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_MandatoryProperties( THIS_ VARIANT FAR *retval ) { HRESULT hr = S_OK; if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); VariantInit( retval ); if ( _fNTDS ) { GET_PROPERTY_VARIANT( this, mustContain ); } else if ( _pClassInfo ) { hr = MakeVariantFromPropStringTable( _pClassInfo->pOIDsMustContain, _hSchema, retval ); } else { hr = MakeVariantFromStringArray( NULL, retval ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_MandatoryProperties( THIS_ VARIANT vMandatoryProperties ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_Property( this, TEXT("mustContain"), vMandatoryProperties ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_OptionalProperties( THIS_ VARIANT FAR *retval ) { HRESULT hr = S_OK; if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); VariantInit( retval ); if ( _fNTDS ) { GET_PROPERTY_VARIANT( this, mayContain ); } else if ( _pClassInfo ) { hr = MakeVariantFromPropStringTable( _pClassInfo->pOIDsMayContain, _hSchema, retval ); } else { hr = MakeVariantFromStringArray( NULL, retval ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_OptionalProperties( THIS_ VARIANT vOptionalProperties ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_Property( this, TEXT("mayContain"), vOptionalProperties ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_NamingProperties( THIS_ VARIANT FAR *pvNamingProperties ) { HRESULT hr = S_OK; if ( !pvNamingProperties ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); VariantInit( pvNamingProperties ); hr = get_VARIANT_Property( this, TEXT("rDNAttId"), pvNamingProperties ); if ( SUCCEEDED(hr) ) RRETURN(hr); hr = get_NTDSProp_Helper( TEXT("rDNAttId"), pvNamingProperties ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_NamingProperties( THIS_ VARIANT vNamingProperties ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_Property( this, TEXT("rDNAttId"), vNamingProperties ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_DerivedFrom( THIS_ VARIANT FAR *retval ) { HRESULT hr = S_OK; if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); VariantInit( retval ); if ( _fNTDS ) { GET_PROPERTY_VARIANT( this, subClassOf ); } else if ( _pClassInfo ) { hr = MakeVariantFromStringArray( _pClassInfo->pOIDsSuperiorClasses, retval ); } else { hr = MakeVariantFromStringArray( NULL, retval ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_DerivedFrom( THIS_ VARIANT vDerivedFrom ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_Property( this, TEXT("subClassOf"), vDerivedFrom ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_AuxDerivedFrom( THIS_ VARIANT FAR *retval ) { HRESULT hr = S_OK; if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); VariantInit( retval ); if ( _fNTDS ) { GET_PROPERTY_VARIANT( this, auxiliaryClass ); } else if ( _pClassInfo ) { hr = MakeVariantFromStringArray( _pClassInfo->pOIDsAuxClasses, retval ); } else { hr = MakeVariantFromStringArray( NULL, retval ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_AuxDerivedFrom( THIS_ VARIANT vAuxDerivedFrom ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_Property( this, TEXT("auxiliaryClass"), vAuxDerivedFrom ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_PossibleSuperiors( THIS_ VARIANT FAR *pvPossSuperiors) { HRESULT hr = S_OK; VARIANT vSysPossSuperiors; DWORD dwSyntaxId; DWORD dwStatus = 0; // Boolean values used to indicate if the values exist BOOL fpossSuperiors = FALSE; BOOL fsysPossSuperiors = FALSE; // Used in case we need a union of the values LDAPOBJECTARRAY ldapSrcObjects1; LDAPOBJECTARRAY ldapSrcObjects2; LDAPOBJECTARRAY_INIT(ldapSrcObjects1); LDAPOBJECTARRAY_INIT(ldapSrcObjects2); VariantInit(&vSysPossSuperiors); if ( !pvPossSuperiors ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); VariantInit( pvPossSuperiors ); hr = get_VARIANT_Property( this, TEXT("possSuperiors"), pvPossSuperiors ); if ( SUCCEEDED(hr) ){ fpossSuperiors = TRUE; } hr = get_VARIANT_Property( this, TEXT("systemPossSuperiors"), &vSysPossSuperiors ); if (SUCCEEDED(hr)) { fsysPossSuperiors = TRUE; } if (!fpossSuperiors) { hr = get_NTDSProp_Helper( TEXT("possSuperiors"), pvPossSuperiors ); if (SUCCEEDED(hr)) { fpossSuperiors = TRUE; } } if (!fsysPossSuperiors) { hr = get_NTDSProp_Helper( TEXT("systemPossSuperiors"), &vSysPossSuperiors ); if (SUCCEEDED(hr)) { fsysPossSuperiors = TRUE; } } // Now if both are true, we need to do a union if (fpossSuperiors && fsysPossSuperiors) { // need to do the union // it is easier for me to handle strings in the ldap format // than to handle them as variants as there are helpers available // for that already hr = _pPropertyCache->unboundgetproperty( L"possSuperiors", &dwSyntaxId, &dwStatus, &ldapSrcObjects1 ); // No compatibility -- below it resets hr if (hr == E_FAIL) { hr = S_OK; } BAIL_ON_FAILURE(hr); hr = _pPropertyCache->unboundgetproperty( L"systemPossSuperiors", &dwSyntaxId, &dwStatus, &ldapSrcObjects2 ); // No compatibility -- below it resets hr if (hr == E_FAIL) { hr = S_OK; } BAIL_ON_FAILURE(hr); // Clear them as we no longer need the data here VariantClear(pvPossSuperiors); VariantClear(&vSysPossSuperiors); hr = makeUnionVariantFromLdapObjects( ldapSrcObjects1, ldapSrcObjects2, pvPossSuperiors ); BAIL_ON_FAILURE(hr); } else if (fpossSuperiors || fsysPossSuperiors) { // return the appropriate value in the variant if (fsysPossSuperiors) { hr = VariantCopy(pvPossSuperiors, &vSysPossSuperiors); VariantClear(&vSysPossSuperiors); BAIL_ON_FAILURE(hr); } } error: LdapTypeFreeLdapObjects(&ldapSrcObjects1); LdapTypeFreeLdapObjects(&ldapSrcObjects2); // this will make sure we handle fall through correctly if (SUCCEEDED(hr)) { RRETURN(hr); } VariantClear(pvPossSuperiors); VariantClear(&vSysPossSuperiors); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_PossibleSuperiors( THIS_ VARIANT vPossSuperiors) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_Property( this, TEXT("possSuperiors"), vPossSuperiors); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::get_Containment( THIS_ VARIANT *pvContainment ) { HRESULT hr = S_OK; LPTSTR *aValues = NULL; if ( !pvContainment ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); VariantInit( pvContainment ); // // This call will fetch possibleInferiors if necessary using // an implicit GetInfo. // hr = GetEx(L"possibleInferiors", pvContainment); if (FAILED(hr) && (hr == E_ADS_PROPERTY_NOT_FOUND) ) { // // In this case we need to return an empty array // hr = MakeVariantFromStringArray( aValues, pvContainment ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_Containment( THIS_ VARIANT vContainment) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPClass::get_Container( THIS_ VARIANT_BOOL FAR *pfContainer ) { HRESULT hr = S_OK; VARIANT vVar; if (!pfContainer) { RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); } *pfContainer = VARIANT_FALSE; if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); VariantInit(&vVar); // // We now cache possibleInferiors as part of a GetInfo call. // hr = GetEx(L"possibleInferiors", &vVar); if (SUCCEEDED(hr)) { // // Need to see if there were any values and not just NULL. // if (V_VT(&vVar) != (VT_VARIANT | VT_ARRAY)) { // // Not the expected result has // *pfContainer = VARIANT_FALSE; } else { // // Need to see how many elements are there. // LONG lnLBound = 0, lnUBound = 0; hr = SafeArrayGetUBound( V_ARRAY(&vVar), 1, &lnUBound ); if (SUCCEEDED(hr)) { hr = SafeArrayGetLBound( V_ARRAY(&vVar), 1, &lnLBound ); if (SUCCEEDED(hr)) { // // Check the length and make sure it is not 0 vals. // if ((lnUBound - lnLBound) + 1) { *pfContainer = VARIANT_TRUE; } } else { // // Default to not container in this case // *pfContainer = VARIANT_FALSE; } } hr = S_OK; } // we need to release the memory in vVar VariantClear(&vVar); } else if (FAILED(hr) && (hr == E_ADS_PROPERTY_NOT_FOUND) ) { *pfContainer = VARIANT_FALSE; hr = S_OK; } // // Anything other than these and we should return the // appropriate hr back. // RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_Container( THIS_ VARIANT_BOOL fContainer ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_SUPPORTED); } STDMETHODIMP CLDAPClass::get_HelpFileName( THIS_ BSTR FAR *pbstrHelpFileName ) { if ( !pbstrHelpFileName ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); HRESULT hr; hr = ADsAllocString( _bstrHelpFileName? _bstrHelpFileName: TEXT(""), pbstrHelpFileName ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPClass::put_HelpFileName( THIS_ BSTR bstrHelpFile ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_SUPPORTED); #if 0 RRETURN( ADsReAllocString( &_bstrHelpFileName, bstrHelpFile? bstrHelpFile: TEXT("") )); #endif } STDMETHODIMP CLDAPClass::get_HelpFileContext( THIS_ long FAR *plHelpContext ) { if ( !plHelpContext ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); *plHelpContext = _lHelpFileContext; RRETURN(S_OK); } STDMETHODIMP CLDAPClass::put_HelpFileContext( THIS_ long lHelpContext ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_SUPPORTED); #if 0 _lHelpFileContext = lHelpContext; RRETURN(S_OK); #endif } STDMETHODIMP CLDAPClass::Qualifiers(THIS_ IADsCollection FAR* FAR* ppQualifiers) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } HRESULT CLDAPClass::AllocateClassObject( CCredentials& Credentials, CLDAPClass FAR * FAR * ppClass ) { CLDAPClass FAR *pClass = NULL; CAggregatorDispMgr FAR *pDispMgr = NULL; CPropertyCache FAR *pPropertyCache = NULL; HRESULT hr = S_OK; pClass = new CLDAPClass(); if ( pClass == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); pDispMgr = new CAggregatorDispMgr(Credentials); if ( pDispMgr == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADs, (IADs *) pClass, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADsClass, (IADsClass *) pClass, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = CPropertyCache::createpropertycache( (CCoreADsObject FAR *) pClass, (IGetAttributeSyntax *) pClass, &pPropertyCache ); BAIL_ON_FAILURE(hr); pDispMgr->RegisterPropertyCache(pPropertyCache); pClass->_Credentials = Credentials; pClass->_pDispMgr = pDispMgr; pClass->_pPropertyCache = pPropertyCache; *ppClass = pClass; RRETURN(hr); error: delete pDispMgr; delete pClass; RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPClass::FindModifications( int *pOIDs, DWORD nNumOfOids, LPTSTR pszPropName, LDAPModW ***aMods, DWORD *pdwNumOfMods ) { HRESULT hr = S_OK; int *pOIDsOld = _tcsicmp(pszPropName, TEXT("mustContain")) == 0 ? _pClassInfo->pOIDsMustContain : _pClassInfo->pOIDsMayContain; DWORD i = 0; DWORD j = 0; DWORD k = 0; BOOL fReadProperty = FALSE; int *pOIDsCurrent = NULL; DWORD nNumOfOidsCurrent = 0; BOOL fFound = FALSE; LPTSTR *aValuesAdd = NULL; LPTSTR *pTemp = NULL; DWORD nValuesAdd = 0; DWORD nValuesAddTotal = 10; LPTSTR *aValuesRemove = NULL; DWORD nValuesRemove = 0; DWORD nValuesRemoveTotal = 10; DWORD nIndex = 0; LDAPMessage *res = NULL; LDAPMessage *e = NULL; LPTSTR aStrings[] = { TEXT("cn"), pszPropName, NULL }; aValuesAdd = (LPTSTR *) AllocADsMem(sizeof(LPTSTR) * nValuesAddTotal ); if ( aValuesAdd == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } aValuesRemove = (LPTSTR *) AllocADsMem(sizeof(LPTSTR) * nValuesRemoveTotal); if ( aValuesRemove == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } // // We need to find the differences between the properties that needs to be // set and the properties that is currently on the server. // while ((pOIDs[j] != -1) || (pOIDsOld[k] != -1)) { if ( pOIDs[j] == pOIDsOld[k] ) { // No changes here j++; k++; } else if ( ( pOIDsOld[k] == -1 ) || ( ( pOIDs[j] != -1 ) && ( pOIDs[j] < pOIDsOld[k] )) ) { // // A new property has been added. // if ( nValuesAdd == nValuesAddTotal ) { pTemp = (LPTSTR *) ReallocADsMem( aValuesAdd, sizeof(LPTSTR) * nValuesAddTotal, sizeof(LPTSTR) * nValuesAddTotal * 2 ); if ( pTemp == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } aValuesAdd = pTemp; pTemp = NULL; nValuesAddTotal *= 2; } nIndex = (((SCHEMAINFO *)_hSchema)->aPropertiesSearchTable[pOIDs[j]]).nIndex; aValuesAdd[nValuesAdd++] = (((SCHEMAINFO *)_hSchema)->aProperties[nIndex]).pszPropertyName; j++; } else // ( pOIDs[j] == -1 || pOIDs[j] > pOIDsOld[k] ) { // Some property has been removed, we need to read the current class // set of "mayContain" or "mustContain" to make sure we // aren't removing any "systemMustContain" or "systemMayContain" // and we are not trying to remove any parent classes // may/mustContain if ( !fReadProperty ) { LPTSTR *aValues = NULL; int nCount = 0; if ( _pszLDAPDn == NULL ) { hr = BuildSchemaLDAPPathAndGetAttribute( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, _pClassInfo == NULL, _Credentials, aStrings, &_pszLDAPServer, &_pszLDAPDn, &_ld, &res ); BAIL_ON_FAILURE(hr); hr = LdapFirstEntry( _ld, res, &e ); BAIL_ON_FAILURE(hr); hr = LdapGetValues( _ld, e, pszPropName, &aValues, &nCount ); } else { hr = LdapReadAttribute( _pszLDAPServer, _pszLDAPDn, pszPropName, &aValues, &nCount, _Credentials, _dwPort ); } if ( hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE) ) { hr = S_OK; } BAIL_ON_FAILURE(hr); fReadProperty = TRUE; if ( nCount > 0 ) { hr = MakePropArrayFromStringArray( aValues, nCount, (SCHEMAINFO *) _hSchema, &pOIDsCurrent, &nNumOfOidsCurrent ); LdapValueFree( aValues ); BAIL_ON_FAILURE(hr); } } // // See if we can find the property that we want to remove from. // We don't need to reset i since both arrays are sorted. // for ( fFound = FALSE; i < nNumOfOidsCurrent; i++ ) { if ( pOIDsOld[k] == pOIDsCurrent[i] ) { fFound = TRUE; break; } else if ( pOIDsOld[k] < pOIDsCurrent[i] ) { // Both arrays are sorted, so we can break here break; } } if ( nNumOfOidsCurrent == 0 || !fFound ) { int err = NO_ERROR; // This property is not in "mustContain" or "mayContain", // so nothing can be removed hr = E_ADS_SCHEMA_VIOLATION; BAIL_ON_FAILURE(hr); } // // Modify the request to remove the property here // if ( nValuesRemove == nValuesRemoveTotal ) { pTemp = (LPTSTR *) ReallocADsMem( aValuesRemove, sizeof(LPTSTR) * nValuesRemoveTotal, sizeof(LPTSTR) * nValuesRemoveTotal * 2 ); if ( pTemp == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } aValuesRemove = pTemp; pTemp = NULL; nValuesRemoveTotal *= 2; } nIndex = (((SCHEMAINFO *)_hSchema)->aPropertiesSearchTable[pOIDsOld[k]]).nIndex; aValuesRemove[nValuesRemove++] = (((SCHEMAINFO *)_hSchema)->aProperties[nIndex]).pszPropertyName; k++; } } if ( nValuesAdd == 0 ) { FreeADsMem( aValuesAdd ); aValuesAdd = NULL; } if ( nValuesRemove == 0 ) { FreeADsMem( aValuesRemove ); aValuesRemove = NULL; } if ( aValuesAdd || aValuesRemove ) { hr = AddModifyRequest( aMods, pdwNumOfMods, pszPropName, aValuesAdd, aValuesRemove ); } error: if ( pOIDsCurrent ) FreeADsMem( pOIDsCurrent ); if (res) LdapMsgFree(res); if ( FAILED(hr)) { if ( aValuesAdd ) FreeADsMem( aValuesAdd ); if ( aValuesRemove ) FreeADsMem( aValuesRemove ); } RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPClass::AddModifyRequest( LDAPModW ***aMods, DWORD *pdwNumOfMods, LPTSTR pszPropName, LPTSTR *aValuesAdd, LPTSTR *aValuesRemove ) { HRESULT hr = S_OK; LDAPModW *aModsBuffer = NULL; DWORD j = 0; DWORD nCount = 0; if ( aValuesAdd != NULL ) nCount++; if ( aValuesRemove != NULL ) nCount++; if ( nCount == 0 ) RRETURN(S_OK); if ( *aMods == NULL ) { *aMods = (LDAPModW **) AllocADsMem( (nCount + 1) * sizeof(LDAPModW *)); if ( *aMods == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } aModsBuffer = (LDAPModW *) AllocADsMem( nCount * sizeof(LDAPModW)); if ( aModsBuffer == NULL ) { FreeADsMem( *aMods ); *aMods = NULL; hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } } else { LDAPModW **aModsTemp = NULL; aModsTemp = (LDAPModW **) AllocADsMem( (*pdwNumOfMods + nCount + 1) * sizeof(LDAPModW *)); if ( aModsTemp == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } aModsBuffer = (LDAPModW *) AllocADsMem( (*pdwNumOfMods + nCount) * sizeof(LDAPModW)); if ( aModsBuffer == NULL ) { FreeADsMem( aModsTemp ); hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } memcpy( aModsBuffer, **aMods, *pdwNumOfMods * sizeof(LDAPModW)); FreeADsMem( **aMods ); FreeADsMem( *aMods ); *aMods = aModsTemp; for ( j = 0; j < *pdwNumOfMods; j++ ) { (*aMods)[j] = &aModsBuffer[j]; } } if ( aValuesAdd ) { (*aMods)[j] = &aModsBuffer[j]; aModsBuffer[j].mod_type = pszPropName; aModsBuffer[j].mod_values = aValuesAdd; aModsBuffer[j].mod_op |= LDAP_MOD_ADD; j++; } if ( aValuesRemove ) { (*aMods)[j] = &aModsBuffer[j]; aModsBuffer[j].mod_type = pszPropName; aModsBuffer[j].mod_values = aValuesRemove; aModsBuffer[j].mod_op |= LDAP_MOD_DELETE; } *pdwNumOfMods += nCount; error: RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPClass::get_NTDSProp_Helper( THIS_ BSTR bstrName, VARIANT FAR *pvProp ) { HRESULT hr = S_OK; LPTSTR *aValues = NULL; int nCount = 0; LDAPMessage *res = NULL; LDAPMessage *e = NULL; LPTSTR aStrings[3]; aStrings[0] = TEXT("cn"); aStrings[1] = bstrName; aStrings[2] = NULL; if ( _pClassInfo == NULL ) // new class { hr = MakeVariantFromStringArray( NULL, pvProp ); RRETURN_EXP_IF_ERR(hr); } // // If the dn is NULL we have not got the info so fetch it. // if (_pszLDAPDn == NULL ) { hr = BuildSchemaLDAPPathAndGetAttribute( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, _pClassInfo == NULL, _Credentials, aStrings, &_pszLDAPServer, &_pszLDAPDn, &_ld, &res ); BAIL_IF_ERROR(hr); hr = LdapFirstEntry( _ld, res, &e ); BAIL_IF_ERROR(hr); hr = LdapGetValues( _ld, e, bstrName, &aValues, &nCount ); } else { hr = LdapReadAttribute( _pszLDAPServer, _pszLDAPDn, bstrName, &aValues, &nCount, _Credentials, _dwPort ); } if ( hr == HRESULT_FROM_WIN32(ERROR_DS_NO_ATTRIBUTE_OR_VALUE) ) { hr = NO_ERROR; } BAIL_IF_ERROR(hr); hr = MakeVariantFromStringArray( aValues, pvProp ); BAIL_IF_ERROR(hr); hr = put_VARIANT_Property( this, bstrName, *pvProp ); BAIL_IF_ERROR(hr); hr = _pPropertyCache->ClearPropertyFlag( bstrName ); BAIL_IF_ERROR(hr); cleanup: if ( aValues ) LdapValueFree( aValues ); if (res) LdapMsgFree(res); RRETURN_EXP_IF_ERR(hr); } // // Needed for dynamic dispid's in the property cache. // HRESULT CLDAPClass::GetAttributeSyntax( LPWSTR szPropertyName, PDWORD pdwSyntaxId ) { HRESULT hr; hr = LdapGetSyntaxOfAttributeOnServer( _pszLDAPServer, szPropertyName, pdwSyntaxId, _Credentials, _dwPort ); RRETURN_EXP_IF_ERR(hr); } /* IUmiHelperPrivate support. */ //+--------------------------------------------------------------------------- // Function: CLDAPClass::GetPropertiesHelper // // Synopsis: Returns an array of PPROPERTYINFO that points to the // property definitions this class can hold. // // Arguments: ppProperties - Ret values for the property info. // pdwCount - Ret value for the number of properties. // // // Returns: HRESULT - S_OK or any failure error code. // // Modifies: ppProperties and pdwCount appropriately. // //---------------------------------------------------------------------------- STDMETHODIMP CLDAPClass::GetPropertiesHelper( void **ppProperties, PDWORD pdwPropCount ) { HRESULT hr = S_OK; PPROPERTYINFO *pPropArray = NULL; DWORD dwPropCount = 0; DWORD dwCtr; SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) _hSchema; // // Initialize out params to default values. // *pdwPropCount = 0; *ppProperties = NULL; // // If there is no classInfo then we do not have any further processing // to do as there are no properties on this class. // if (!this->_pClassInfo) { RRETURN(E_FAIL); } // // We need to know how many entries are there in the list of properties. // The total is made up of both the mandatory and optional properties. // Note that we will adjust this value suitably as we process the array. // dwPropCount = _pClassInfo->nNumOfMayContain + _pClassInfo->nNumOfMustContain; pPropArray = (PPROPERTYINFO *) AllocADsMem( sizeof(PPROPERTY*) * dwPropCount ); if (!pPropArray) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } // // Go through and get the info for the must contain. // for ( dwCtr = 0; ((dwCtr < (_pClassInfo->nNumOfMustContain)) && (_pClassInfo->pOIDsMustContain[dwCtr] != -1)); dwCtr++) { // // Assign the appropriate prop info ptrs from the may contain list. // pPropArray[dwCtr] = &(pSchemaInfo->aProperties[ (pSchemaInfo->aPropertiesSearchTable[ _pClassInfo->pOIDsMustContain[dwCtr]].nIndex )]); } DWORD dwAdjust; // // We could have less than the number in the array if we hit -1, or // -1 could be pointing correctly to the last element !!! // if ((_pClassInfo->pOIDsMustContain[dwCtr] == -1) && (dwCtr < _pClassInfo->nNumOfMustContain)) { dwCtr++; } dwAdjust = dwCtr; // // Now get the may contain information. // for ( dwCtr = 0; ((dwCtr < (_pClassInfo->nNumOfMayContain)) && (_pClassInfo->pOIDsMayContain[dwCtr]) != -1); dwCtr++) { DWORD dwTemp = dwCtr + dwAdjust; pPropArray[dwTemp] = &(pSchemaInfo->aProperties[ (pSchemaInfo->aPropertiesSearchTable[ _pClassInfo->pOIDsMayContain[dwCtr]].nIndex )]); } *ppProperties = (void *) pPropArray; if ((_pClassInfo->pOIDsMayContain[dwCtr] == -1) && (dwCtr < _pClassInfo->nNumOfMustContain)) { *pdwPropCount = dwAdjust + dwCtr + 1; } else { *pdwPropCount = dwAdjust + dwCtr; } error: if (FAILED(hr)) { if (pPropArray) { FreeADsMem(pPropArray); } } RRETURN(hr); } HRESULT IsPropertyInList( LPCWSTR pszName, VARIANT vProp, BOOL *pfInList ) { HRESULT hr = S_OK; VARIANT *pvProp = &vProp; SAFEARRAY *pArray = V_ARRAY(pvProp); DWORD dwSLBound; DWORD dwSUBound; DWORD dwLength = 0; VARIANT vVar; VariantInit(&vVar); *pfInList = FALSE; hr = SafeArrayGetLBound(pArray, 1, (long FAR *)&dwSLBound ); BAIL_ON_FAILURE(hr); hr = SafeArrayGetUBound(pArray, 1, (long FAR *)&dwSUBound ); BAIL_ON_FAILURE(hr); dwLength = dwSUBound - dwSLBound; // // If there are 0 elements in cannot be in the list. // if (!dwLength) { RRETURN(S_OK); } for (DWORD dwCtr = 0; (dwCtr <= dwLength) && (*pfInList != TRUE); dwCtr++) { // // Go through the array to see if we find the name in the list. // VariantClear(&vVar); hr = SafeArrayGetElement(pArray, (long FAR *)&dwCtr, &vVar ); BAIL_ON_FAILURE(hr); if (V_VT(&vVar) != VT_BSTR) { BAIL_ON_FAILURE(hr = E_FAIL); } if (!_wcsicmp(pszName, vVar.bstrVal)) { *pfInList = TRUE; } } error: VariantClear(&vVar); RRETURN(hr); } //+--------------------------------------------------------------------------- // Function: CLDAPClass::GetOriginHelper // // Synopsis: Returns the name of the class this property originated on. // // Arguments: pszName - Name of the property whose origin is needed. // pbstrOrigin - Return value - name of class. // // Returns: HRESULT - S_OK or any failure error code. // // Modifies: pbstrOrigin on success. // //---------------------------------------------------------------------------- STDMETHODIMP CLDAPClass::GetOriginHelper( LPCWSTR pszName, BSTR *pbstrOrigin ) { HRESULT hr = S_OK; CCredentials cCreds = _Credentials; IUnknown *pUnk = NULL; IADsContainer *pContainer = NULL; BSTR bstrTemp = NULL; DWORD dwAuthFlags = cCreds.GetAuthFlags(); BOOL fDone = FALSE; BOOL fInMustContain = FALSE; BOOL fInList = FALSE; IADsClass *pClass = NULL; IDispatch *pDispObj = NULL; BOOL fMustContain = FALSE; VARIANT vVar, vVarProps; VariantInit(&vVar); VariantInit(&vVarProps); // // If we are already at the top. // if (!_wcsicmp(_Name, L"top")) { hr = ADsAllocString(L"top", pbstrOrigin); RRETURN(hr); } // // We want to chase up either the mandatory or optional list and // not both. So we first update this flag. // hr = get_MandatoryProperties(&vVarProps); BAIL_ON_FAILURE(hr); hr = IsPropertyInList(pszName, vVarProps, &fInMustContain); BAIL_ON_FAILURE(hr) // // Mask out the reserved flags - we want to be in ADSI land now. // cCreds.SetAuthFlags(dwAuthFlags & ~(ADS_AUTH_RESERVED)); // // This will be the default class name to return. // hr = ADsAllocString(_Name, &bstrTemp); BAIL_ON_FAILURE(hr); // // Need to get hold of the schema container. // hr = GetObject(_Parent, cCreds, (void **)&pUnk); BAIL_ON_FAILURE(hr); hr = pUnk->QueryInterface(IID_IADsContainer, (void **) &pContainer); BAIL_ON_FAILURE(hr); while (!fDone) { // // Need to keep finding the derived from until we hit top // or we hit a class that does not support the attribute. // VariantClear(&vVar); VariantClear(&vVarProps); if (pDispObj) { pDispObj->Release(); pDispObj = NULL; } if (!pClass) { // // Need to get the derived from for the current class. // hr = get_DerivedFrom(&vVar); } else { hr = pClass->get_DerivedFrom(&vVar); pClass->Release(); pClass = NULL; } // // Get the derived from classes object. // hr = pContainer->GetObject( L"Class", vVar.bstrVal, &pDispObj ); BAIL_ON_FAILURE(hr); hr = pDispObj->QueryInterface( IID_IADsClass, (void **) &pClass ); BAIL_ON_FAILURE(hr); if (!_wcsicmp(vVar.bstrVal, L"top")) { fDone = TRUE; } if (fInMustContain) { hr = pClass->get_MandatoryProperties(&vVarProps); } else { hr = pClass->get_OptionalProperties(&vVarProps); } BAIL_ON_FAILURE(hr); hr = IsPropertyInList(pszName, vVarProps, &fInList); BAIL_ON_FAILURE(hr); if (!fInList) { // // The value in temp is the correct class name // hr = ADsAllocString(bstrTemp, pbstrOrigin); BAIL_ON_FAILURE(hr); fDone = TRUE; } // // This will be true only if we found the item in top. // if (fInList && fDone) { hr = ADsAllocString(L"Top", pbstrOrigin); BAIL_ON_FAILURE(hr); } if (bstrTemp) { SysFreeString(bstrTemp); bstrTemp = NULL; } // // Need to get the current class name in bstrTemp. // hr = ADsAllocString(vVar.bstrVal, &bstrTemp); BAIL_ON_FAILURE(hr); } // // We will default to the current class. // if (!pbstrOrigin) { hr = ADsAllocString(_Name, pbstrOrigin); BAIL_ON_FAILURE(hr); } error: if (bstrTemp) { SysFreeString(bstrTemp); } VariantClear(&vVar); VariantClear(&vVarProps); if (pContainer) { pContainer->Release(); } if (pUnk) { pUnk->Release(); } if (pClass) { pClass->Release(); } if (pDispObj) { pDispObj->Release(); } RRETURN(hr); } /******************************************************************/ /* Class CLDAPProperty /******************************************************************/ DEFINE_IDispatch_Implementation(CLDAPProperty) DEFINE_IADs_Implementation(CLDAPProperty) CLDAPProperty::CLDAPProperty() : _pDispMgr( NULL ), _pPropertyCache( NULL ), _bstrSyntax( NULL ), _hSchema( NULL ), _pPropertyInfo( NULL ), _pszLDAPServer(NULL), _pszLDAPDn(NULL), _fNTDS( TRUE ), _ld( NULL ) { ENLIST_TRACKING(CLDAPProperty); } CLDAPProperty::~CLDAPProperty() { delete _pDispMgr; delete _pPropertyCache; if ( _bstrSyntax ) { ADsFreeString( _bstrSyntax ); } if ( _hSchema ) { SchemaClose( &_hSchema ); _hSchema = NULL; } if (_pszLDAPServer) { FreeADsStr(_pszLDAPServer); } if (_pszLDAPDn) { FreeADsStr(_pszLDAPDn); } if ( _ld ) { LdapCloseObject( _ld ); _ld = NULL; } } HRESULT CLDAPProperty::CreateProperty( BSTR bstrParent, LDAP_SCHEMA_HANDLE hSchema, BSTR bstrName, PROPERTYINFO *pPropertyInfo, CCredentials& Credentials, DWORD dwObjectState, REFIID riid, void **ppvObj ) { CLDAPProperty FAR * pProperty = NULL; HRESULT hr = S_OK; BSTR bstrSyntax = NULL; OBJECTINFO ObjectInfo; POBJECTINFO pObjectInfo = &ObjectInfo; memset(pObjectInfo, 0, sizeof(OBJECTINFO)); hr = AllocatePropertyObject(Credentials, &pProperty ); BAIL_ON_FAILURE(hr); pProperty->_pPropertyInfo = pPropertyInfo; SchemaAddRef( hSchema ); pProperty->_hSchema = hSchema; if ( pPropertyInfo ) { hr = put_BSTR_Property( pProperty, TEXT("attributeID"), pPropertyInfo->pszOID); if ( SUCCEEDED(hr)) { hr = put_VARIANT_BOOL_Property( pProperty, TEXT("isSingleValued"), (VARIANT_BOOL)pPropertyInfo->fSingleValued ); BAIL_ON_FAILURE(hr); pProperty->_pPropertyCache->ClearAllPropertyFlags(); pProperty->_fNTDS = TRUE; } else { pProperty->_fNTDS = FALSE; } } hr = ADsObject(bstrParent, pObjectInfo); BAIL_ON_FAILURE(hr); pProperty->_dwPort = pObjectInfo->PortNumber; FreeObjectInfo(pObjectInfo); pObjectInfo = NULL; hr = pProperty->InitializeCoreObject( bstrParent, bstrName, PROPERTY_CLASS_NAME, CLSID_LDAPProperty, dwObjectState ); BAIL_ON_FAILURE(hr); // // At this point update the info in the property cache // pProperty->_pPropertyCache->SetObjInformation( &(pProperty->_Credentials), pProperty->_pszLDAPServer, pProperty->_dwPort ); BAIL_ON_FAILURE(hr); // // Need to create the umi object if applicable. // if (Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) { hr = ((CCoreADsObject*)pProperty)->InitUmiObject( IntfPropsSchema, pProperty->_pPropertyCache, (IADs *) pProperty, (IADs *) pProperty, riid, ppvObj, &(pProperty->_Credentials), pProperty->_dwPort, pProperty->_pszLDAPServer, pProperty->_pszLDAPDn ); BAIL_ON_FAILURE(hr); // // Need to put syntax in the cache. // if (pProperty->_pPropertyInfo->pszSyntax) { hr = GetFriendlyNameFromOID( pProperty->_pPropertyInfo->pszSyntax, &bstrSyntax ); if (FAILED(hr)) { // // ok if this failed. // hr = S_OK; } else { hr = put_BSTR_Property( pProperty, TEXT("syntax"), bstrSyntax ); SysFreeString(bstrSyntax); // // Not critical failure // hr = S_OK; } } // // Name is a simulated propert used for UMI. // hr = put_BSTR_Property( pProperty, TEXT("Name"), bstrName ); BAIL_ON_FAILURE(hr); RRETURN(S_OK); } // // Get the LDAP path of the schema entry // hr = pProperty->QueryInterface( riid, ppvObj ); BAIL_ON_FAILURE(hr); pProperty->Release(); RRETURN(hr); error: *ppvObj = NULL; delete pProperty; FreeObjectInfo(pObjectInfo); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::QueryInterface(REFIID iid, LPVOID FAR* ppv) { if (ppv == NULL) { RRETURN(E_POINTER); } if (IsEqualIID(iid, IID_IUnknown)) { *ppv = (IADsProperty FAR *) this; } else if (IsEqualIID(iid, IID_IDispatch)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_ISupportErrorInfo)) { *ppv = (ISupportErrorInfo FAR *) this; } else if (IsEqualIID(iid, IID_IADs)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_IADsProperty)) { *ppv = (IADsProperty FAR *) this; } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } /* ISupportErrorInfo method */ STDMETHODIMP CLDAPProperty::InterfaceSupportsErrorInfo(THIS_ REFIID riid) { if (IsEqualIID(riid, IID_IADs) | IsEqualIID(riid, IID_IADsProperty)) { RRETURN(S_OK); } else { RRETURN(S_FALSE); } } /* IADs methods */ STDMETHODIMP CLDAPProperty::SetInfo(THIS) { HRESULT hr = S_OK; BOOL fChanged = FALSE; if ( !_fNTDS ) RRETURN_EXP_IF_ERR(E_NOTIMPL); if ( GetObjectState() == ADS_OBJECT_UNBOUND ) { hr = LDAPCreateObject(); BAIL_ON_FAILURE(hr); fChanged = TRUE; // // If the create succeded, set the object type to bound // SetObjectState(ADS_OBJECT_BOUND); } else { hr = LDAPSetObject( &fChanged ); BAIL_ON_FAILURE(hr); } // // Need to refresh the schema // if ( SUCCEEDED(hr) && fChanged ) hr = LDAPRefreshSchema(); error: RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPProperty::LDAPSetObject( BOOL *pfChanged ) { HRESULT hr = S_OK; LDAPModW **aMod = NULL; BOOL fNTSecDes = FALSE; SECURITY_INFORMATION NewSeInfo; *pfChanged = FALSE; hr = _pPropertyCache->LDAPMarshallProperties( &aMod, &fNTSecDes, &NewSeInfo ); BAIL_ON_FAILURE(hr); if ( aMod == NULL ) // There are no changes that needs to be modified RRETURN(S_OK); if ( _pszLDAPDn == NULL ) { hr = BuildSchemaLDAPPath( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, &_pszLDAPServer, &_pszLDAPDn, _pPropertyInfo == NULL, &_ld, _Credentials ); BAIL_ON_FAILURE(hr); } if ( _ld == NULL ) { hr = LdapOpenObject( _pszLDAPServer, _pszLDAPDn, &_ld, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); } hr = LdapModifyS( _ld, _pszLDAPDn, aMod ); BAIL_ON_FAILURE(hr); // We are successful at this point, // So, clean up the flags in the cache so the same operation // won't be repeated on the next SetInfo() _pPropertyCache->ClearAllPropertyFlags(); *pfChanged = TRUE; error: if (aMod) { if ( *aMod ) FreeADsMem( *aMod ); FreeADsMem( aMod ); } RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPProperty::LDAPCreateObject() { HRESULT hr = S_OK; LDAPModW **aMod = NULL; DWORD dwIndex = 0; VARIANT v; BOOL fNTSecDes= FALSE; SECURITY_INFORMATION NewSeInfo; // // Get the LDAP path of the schema entry // if ( (_pszLDAPServer == NULL) && (_pszLDAPDn == NULL)) { hr = BuildSchemaLDAPPath( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, &_pszLDAPServer, &_pszLDAPDn, _pPropertyInfo == NULL, &_ld, _Credentials ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("objectClass"), &dwIndex ) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_BSTR; V_BSTR(&v) = NT_SCHEMA_PROPERTY_NAME; hr = Put( TEXT("objectClass"), v ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("cn"), &dwIndex ) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_BSTR; V_BSTR(&v) = _Name; hr = Put( TEXT("cn"), v ); BAIL_ON_FAILURE(hr); } if ( _pPropertyCache->findproperty( TEXT("lDAPDisplayName"), &dwIndex ) == E_ADS_PROPERTY_NOT_FOUND ) { VariantInit(&v); v.vt = VT_BSTR; V_BSTR(&v) = _Name; hr = Put( TEXT("lDAPDisplayName"), v ); BAIL_ON_FAILURE(hr); } hr = _pPropertyCache->LDAPMarshallProperties( &aMod, &fNTSecDes, &NewSeInfo ); BAIL_ON_FAILURE(hr); if ( _ld == NULL ) { hr = LdapOpenObject( _pszLDAPServer, _pszLDAPDn, &_ld, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); } hr = LdapAddS( _ld, _pszLDAPDn, aMod ); BAIL_ON_FAILURE(hr); // We are successful at this point, // So, clean up the flags in the cache so the same operation // won't be repeated on the next SetInfo() _pPropertyCache->ClearAllPropertyFlags(); error: if (aMod) { if ( *aMod ) FreeADsMem( *aMod ); FreeADsMem( aMod ); } RRETURN_EXP_IF_ERR(hr); } HRESULT CLDAPProperty::LDAPRefreshSchema(THIS) { HRESULT hr = S_OK; if (( _pszLDAPServer == NULL) && (_pszLDAPDn == NULL)) { hr = BuildSchemaLDAPPath( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, &_pszLDAPServer, &_pszLDAPDn, _pPropertyInfo == NULL, &_ld, _Credentials ); BAIL_ON_FAILURE(hr); } // // Make the old schema obsolete and get the new schema // We cannot delete the old schema since other objects might have // references to it. // hr = LdapMakeSchemaCacheObsolete( _pszLDAPServer, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); error: RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::GetInfo(THIS) { HRESULT hr = S_OK; BSTR bstrSyntax = NULL; if ( GetObjectState() == ADS_OBJECT_UNBOUND ) RRETURN_EXP_IF_ERR(E_ADS_OBJECT_UNBOUND); hr = LDAPRefreshSchema(); BAIL_ON_FAILURE(hr); SchemaClose( &_hSchema ); hr = SchemaOpen( _pszLDAPServer, &_hSchema, _Credentials, _dwPort ); BAIL_ON_FAILURE(hr); // // Find the new property info in the new schemainfo // hr = SchemaGetPropertyInfo( _hSchema, _Name, &_pPropertyInfo ); BAIL_ON_FAILURE( hr ); if ( _pPropertyInfo == NULL ) { // Property name not found, set error hr = E_ADS_BAD_PATHNAME; BAIL_ON_FAILURE(hr); } _pPropertyCache->flushpropertycache(); hr = put_BSTR_Property( this, TEXT("attributeID"), _pPropertyInfo->pszOID); BAIL_ON_FAILURE(hr); if ( _bstrSyntax ) { ADsFreeString( _bstrSyntax ); _bstrSyntax = NULL; } hr = put_VARIANT_BOOL_Property( this, TEXT("isSingleValued"), (VARIANT_BOOL)_pPropertyInfo->fSingleValued ); BAIL_ON_FAILURE(hr); // // If we are calling from Umi land then we need to set // additional properties. // if (_Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) { if (_pPropertyInfo->pszSyntax) { hr = GetFriendlyNameFromOID( _pPropertyInfo->pszSyntax, &bstrSyntax ); if (FAILED(hr)) { // // ok if this failed. // hr = S_OK; } else { hr = put_BSTR_Property( this, TEXT("syntax"), bstrSyntax ); SysFreeString(bstrSyntax); // // Not critical failure // hr = S_OK; } } // // Name is a simulated propert used for UMI. // hr = put_BSTR_Property( this, TEXT("Name"), _Name ); BAIL_ON_FAILURE(hr); } // special props for Umi. if (_fNTDS) { hr = GetNTDSSchemaInfo(TRUE); } _pPropertyCache->ClearAllPropertyFlags(); _pPropertyCache->setGetInfoFlag(); error: if (bstrSyntax) { SysFreeString(bstrSyntax); } RRETURN_EXP_IF_ERR(hr); } // // Helper function for Umi - defined in CCoreADsObject. // STDMETHODIMP CLDAPProperty::GetInfo(DWORD dwFlags) { HRESULT hr = S_OK; if (dwFlags == GETINFO_FLAG_EXPLICIT) { RRETURN(GetInfo()); } else if (_fNTDS && dwFlags != GETINFO_FLAG_IMPLICIT_AS_NEEDED ) { // // Read the extra NTDS specific schema properties. // hr = GetNTDSSchemaInfo(FALSE); } // // Any other flags means nothing to do. // RRETURN(hr); } HRESULT CLDAPProperty::GetNTDSSchemaInfo( BOOL fForce ) { HRESULT hr = S_OK; LPTSTR aStrings[] = { TEXT("cn"), TEXT("schemaIDGUID"), TEXT("rangeUpper"), TEXT("rangeLower"), NULL }; LDAPMessage *res = NULL; if (_pszLDAPDn == NULL) { // // Need to get the dn for this object and also // the attributes we are interested in. // hr = BuildSchemaLDAPPathAndGetAttribute( _Parent, _Name, ((SCHEMAINFO*)_hSchema)->pszSubSchemaSubEntry, _pPropertyInfo == NULL, _Credentials, aStrings, &_pszLDAPServer, &_pszLDAPDn, &_ld, &res ); } else { // // Looks like we just need the attributes in this case. // hr = LdapSearchS( _ld, _pszLDAPDn, LDAP_SCOPE_BASE, L"(objectClass=*)", aStrings, FALSE, &res ); } BAIL_ON_FAILURE(hr); // // If we succeeded we should unmarshall properties into the cache. // hr = _pPropertyCache->LDAPUnMarshallProperties( _pszLDAPServer, _ld, res, fForce, _Credentials ); BAIL_ON_FAILURE(hr); _pPropertyCache->setGetInfoFlag(); error: if (res) { LdapMsgFree(res); } RRETURN(hr); } STDMETHODIMP CLDAPProperty::Get(THIS_ BSTR bstrName, VARIANT FAR* pvProp) { HRESULT hr = S_OK; DWORD dwSyntaxId; LDAPOBJECTARRAY ldapSrcObjects; DWORD dwStatus = 0; LDAPOBJECTARRAY_INIT(ldapSrcObjects); // // For folks who know now what they do. // if (!pvProp) { BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } // // retrieve data object from cache; if one exists // if ( GetObjectState() == ADS_OBJECT_UNBOUND ) { hr = _pPropertyCache->unboundgetproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_FAIL; } } else { hr = _pPropertyCache->getproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_ADS_PROPERTY_NOT_FOUND; } } BAIL_ON_FAILURE(hr); // // translate the Ldap objects to variants // if ( ldapSrcObjects.dwCount == 1 ) { hr = LdapTypeToVarTypeCopy( _pszLDAPServer, _Credentials, ldapSrcObjects.pLdapObjects, dwSyntaxId, pvProp ); } else { hr = LdapTypeToVarTypeCopyConstruct( _pszLDAPServer, _Credentials, ldapSrcObjects, dwSyntaxId, pvProp ); } BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapSrcObjects ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::Put(THIS_ BSTR bstrName, VARIANT vProp) { HRESULT hr = S_OK; DWORD dwSyntaxId = 0; DWORD dwIndex = 0; LDAPOBJECTARRAY ldapDestObjects; DWORD dwNumValues = 0; VARIANT * pVarArray = NULL; VARIANT * pvProp = NULL; VARIANT vDefProp; VariantInit(&vDefProp); LDAPOBJECTARRAY_INIT(ldapDestObjects); hr = SchemaGetSyntaxOfAttribute( _hSchema, bstrName, &dwSyntaxId ); if (FAILED(hr)) { // // Need to see if this is syntax if so we special case // for Umi Objects. // if (!_wcsicmp(L"syntax", bstrName)) { dwSyntaxId = LDAPTYPE_CASEIGNORESTRING; hr = S_OK; } } BAIL_ON_FAILURE(hr); if ( dwSyntaxId == LDAPTYPE_UNKNOWN ) { hr = E_ADS_CANT_CONVERT_DATATYPE; BAIL_ON_FAILURE(hr); } // // A VT_BYREF|VT_VARIANT may expand to a VT_VARIANT|VT_ARRAY. // We should dereference a VT_BYREF|VT_VARIANT once and see // what's inside. // pvProp = &vProp; if (V_VT(pvProp) == (VT_BYREF|VT_VARIANT)) { pvProp = V_VARIANTREF(&vProp); } if ((V_VT(pvProp) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) || (V_VT(pvProp) == (VT_VARIANT|VT_ARRAY))) { hr = ConvertSafeArrayToVariantArray( *pvProp, &pVarArray, &dwNumValues ); // returns E_FAIL if *pvProp is invalid if (hr == E_FAIL) hr = E_ADS_BAD_PARAMETER; BAIL_ON_FAILURE(hr); pvProp = pVarArray; }else { // // If pvProp is a reference to a fundamental type, // we have to dereference it once. // if (V_ISBYREF(pvProp)) { hr = VariantCopyInd(&vDefProp, pvProp); BAIL_ON_FAILURE(hr); pvProp = &vDefProp; } dwNumValues = 1; } #if 0 // // check if this is a legal property for this object, // hr = ValidatePropertyinCache( szLDAPTreeName, _ADsClass, bstrName, &dwSyntaxId ); BAIL_ON_FAILURE(hr); #endif // // check if the variant maps to the syntax of this property // if ( dwNumValues > 0 ) { hr = VarTypeToLdapTypeCopyConstruct( _pszLDAPServer, _Credentials, dwSyntaxId, pvProp, dwNumValues, &ldapDestObjects ); BAIL_ON_FAILURE(hr); } // // Find this property in the cache // hr = _pPropertyCache->findproperty( bstrName, &dwIndex ); // // If this property does not exist in the // cache, add this property into the cache. // if (FAILED(hr)) { hr = _pPropertyCache->addproperty( bstrName ); BAIL_ON_FAILURE(hr); } // // Now update the property in the cache // hr = _pPropertyCache->putproperty( bstrName, PROPERTY_UPDATE, dwSyntaxId, ldapDestObjects ); BAIL_ON_FAILURE(hr); error: VariantClear(&vDefProp); LdapTypeFreeLdapObjects( &ldapDestObjects ); if (pVarArray) { DWORD i = 0; for (i = 0; i < dwNumValues; i++) { VariantClear(pVarArray + i); } FreeADsMem(pVarArray); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::GetEx(THIS_ BSTR bstrName, VARIANT FAR* pvProp) { HRESULT hr = S_OK; DWORD dwSyntaxId; DWORD dwNumValues; DWORD dwStatus = 0; LDAPOBJECTARRAY ldapSrcObjects; LDAPOBJECTARRAY_INIT(ldapSrcObjects); // // For those who know no not what they do // if (!pvProp) { BAIL_ON_FAILURE(hr = E_ADS_BAD_PARAMETER); } // // retrieve data object from cache; if one exists // if ( GetObjectState() == ADS_OBJECT_UNBOUND ) { hr = _pPropertyCache->unboundgetproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_FAIL; } } else { hr = _pPropertyCache->getproperty( bstrName, &dwSyntaxId, &dwStatus, &ldapSrcObjects ); // For backward compatibility if (!ldapSrcObjects.pLdapObjects && SUCCEEDED(hr)) { hr = E_ADS_PROPERTY_NOT_FOUND; } } BAIL_ON_FAILURE(hr); // // translate the Ldap objects to variants // hr = LdapTypeToVarTypeCopyConstruct( _pszLDAPServer, _Credentials, ldapSrcObjects, dwSyntaxId, pvProp ); BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapSrcObjects ); RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::PutEx(THIS_ long lnControlCode, BSTR bstrName, VARIANT vProp) { HRESULT hr = S_OK; DWORD dwSyntaxId = 0; DWORD dwFlags = 0; DWORD dwIndex = 0; LDAPOBJECTARRAY ldapDestObjects; DWORD dwNumValues = 0; VARIANT * pVarArray = NULL; VARIANT * pvProp = NULL; LDAPOBJECTARRAY_INIT(ldapDestObjects); switch (lnControlCode) { case ADS_PROPERTY_CLEAR: dwFlags = PROPERTY_DELETE; break; case ADS_PROPERTY_UPDATE: dwFlags = PROPERTY_UPDATE; break; default: RRETURN_EXP_IF_ERR(hr = E_ADS_BAD_PARAMETER); } hr = SchemaGetSyntaxOfAttribute( _hSchema, bstrName, &dwSyntaxId ); BAIL_ON_FAILURE(hr); if ( dwSyntaxId == LDAPTYPE_UNKNOWN ) { hr = E_ADS_CANT_CONVERT_DATATYPE; BAIL_ON_FAILURE(hr); } #if 0 // // check if this is a legal property for this object, // hr = ValidatePropertyinCache( szLDAPTreeName, _ADsClass, bstrName, &dwSyntaxId ); BAIL_ON_FAILURE(hr); #endif if ( dwFlags != PROPERTY_DELETE ) { // // A VT_BYREF|VT_VARIANT may expand to a VT_VARIANT|VT_ARRAY. // We should dereference a VT_BYREF|VT_VARIANT once and see // what's inside. // pvProp = &vProp; if (V_VT(pvProp) == (VT_BYREF|VT_VARIANT)) { pvProp = V_VARIANTREF(&vProp); } if ((V_VT(pvProp) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) || (V_VT(pvProp) == (VT_VARIANT|VT_ARRAY))) { hr = ConvertSafeArrayToVariantArray( *pvProp, &pVarArray, &dwNumValues ); // returns E_FAIL if *pvProp is invalid if (hr == E_FAIL) hr = E_ADS_BAD_PARAMETER; BAIL_ON_FAILURE(hr); pvProp = pVarArray; } else { hr = E_FAIL; BAIL_ON_FAILURE(hr); } // // check if the variant maps to the syntax of this property // if ( dwNumValues > 0 ) { hr = VarTypeToLdapTypeCopyConstruct( _pszLDAPServer, _Credentials, dwSyntaxId, pvProp, dwNumValues, &ldapDestObjects ); BAIL_ON_FAILURE(hr); } } // // Find this property in the cache // hr = _pPropertyCache->findproperty( bstrName, &dwIndex ); // // If this property does not exist in the // cache, add this property into the cache. // if (FAILED(hr)) { hr = _pPropertyCache->addproperty( bstrName ); BAIL_ON_FAILURE(hr); } // // Now update the property in the cache // hr = _pPropertyCache->putproperty( bstrName, PROPERTY_UPDATE, dwSyntaxId, ldapDestObjects ); BAIL_ON_FAILURE(hr); error: LdapTypeFreeLdapObjects( &ldapDestObjects ); if (pVarArray) { DWORD i = 0; for (i = 0; i < dwNumValues; i++) { VariantClear(pVarArray + i); } FreeADsMem(pVarArray); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::GetInfoEx(THIS_ VARIANT vProperties, long lnReserved) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } /* IADsProperty methods */ STDMETHODIMP CLDAPProperty::get_OID( THIS_ BSTR FAR *retval ) { HRESULT hr; if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( _fNTDS ) { GET_PROPERTY_BSTR( this, attributeID ); } else if ( _pPropertyInfo ) { hr = ADsAllocString( _pPropertyInfo->pszOID? _pPropertyInfo->pszOID : TEXT(""), retval); } else { hr = ADsAllocString( TEXT(""), retval ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::put_OID( THIS_ BSTR bstrOID ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR(E_NOTIMPL); HRESULT hr = put_BSTR_Property( this, TEXT("attributeID"), bstrOID ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::get_Syntax( THIS_ BSTR FAR *retval ) { HRESULT hr = S_OK; if ( !retval ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( _fNTDS ) { if ( _bstrSyntax ) // New property or syntax has been reset { hr = ADsAllocString( _bstrSyntax, retval); } else if (_pPropertyInfo && !_pPropertyInfo->pszSyntax) { // // New property but syntax has not been set // hr = E_ADS_PROPERTY_NOT_FOUND; } } // // Need to return if hr or retVal as we have what we need // if (FAILED(hr) || _bstrSyntax) { RRETURN_EXP_IF_ERR(hr); } // If we have the syntax in _pPropertyInfo we need to // continue and see if we can get a friendly name to return. if ( _pPropertyInfo ) { if (_pPropertyInfo->pszSyntax) { if (!GetFriendlyNameFromOID( _pPropertyInfo->pszSyntax, retval) ) { // in this case we want to set the retVal // to the OID as we could not find a match hr = ADsAllocString(_pPropertyInfo->pszSyntax, retval); } } } else { hr = ADsAllocString( TEXT(""), retval ); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::put_Syntax( THIS_ BSTR bstrSyntax ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR(E_NOTIMPL); LPTSTR pszOID; DWORD dwOMSyntax; HRESULT hr = S_OK; BYTE btDNWithBinary[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x01, 0x01, 0x01, 0x0B }; BYTE btDNWithString[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x01, 0x01, 0x01, 0x0C }; if ( GetSyntaxOID( bstrSyntax, &pszOID, &dwOMSyntax)) { hr = put_BSTR_Property( this, TEXT("attributeSyntax"), pszOID ); BAIL_ON_FAILURE(hr); hr = put_LONG_Property( this, TEXT("oMSyntax"), dwOMSyntax ); BAIL_ON_FAILURE(hr); if ( _bstrSyntax ) ADsFreeString( _bstrSyntax ); hr = ADsAllocString( bstrSyntax, &_bstrSyntax ); BAIL_ON_FAILURE(hr); // // We need to handle the special case of DNWithBinary // and DNString // if (_wcsicmp(bstrSyntax, L"DNWithBinary") == 0) { // // Need to set additional byte attribute // hr = put_OCTETSTRING_Property( this, TEXT("omObjectClass"), btDNWithBinary, (sizeof(btDNWithBinary)/sizeof(btDNWithBinary[0])) ); BAIL_ON_FAILURE(hr); } else if (_wcsicmp(bstrSyntax, L"DNWithString") == 0) { // // Need to set omObjectClass here too // hr = put_OCTETSTRING_Property( this, TEXT("omObjectClass"), btDNWithString, (sizeof(btDNWithString)/sizeof(btDNWithString[0])) ); BAIL_ON_FAILURE(hr); } } else { // Unknown syntax hr = E_ADS_BAD_PARAMETER; BAIL_ON_FAILURE(hr); } error: if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::get_MaxRange( THIS_ long FAR *plMaxRange ) { HRESULT hr = S_OK; if ( !plMaxRange ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); hr = get_LONG_Property(this, TEXT("rangeUpper"), plMaxRange ); if ( SUCCEEDED(hr) ) RRETURN(hr); if ( _pPropertyInfo == NULL ) // new class { hr = E_ADS_PROPERTY_NOT_SET; RRETURN_EXP_IF_ERR(hr); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::put_MaxRange( THIS_ long lMaxRange ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_LONG_Property( this, TEXT("rangeUpper"), lMaxRange ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::get_MinRange( THIS_ long FAR *plMinRange ) { HRESULT hr = S_OK; if ( !plMinRange ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); hr = get_LONG_Property(this, TEXT("rangeLower"), plMinRange ); if ( SUCCEEDED(hr) ) RRETURN(hr); if ( _pPropertyInfo == NULL ) // new class { hr = E_ADS_PROPERTY_NOT_SET; RRETURN_EXP_IF_ERR(hr); } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::put_MinRange( THIS_ long lMinRange ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_LONG_Property( this, TEXT("rangeLower"), lMinRange ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::get_MultiValued( THIS_ VARIANT_BOOL FAR *pfMultiValued ) { if ( !pfMultiValued ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); HRESULT hr = S_OK; VARIANT_BOOL fSingleValued = FALSE; // by default if ( _fNTDS ) { hr = get_VARIANT_BOOL_Property( this, TEXT("isSingleValued"), &fSingleValued ); BAIL_ON_FAILURE(hr); } else if ( _pPropertyInfo ) { fSingleValued = (VARIANT_BOOL)_pPropertyInfo->fSingleValued; } *pfMultiValued = fSingleValued? VARIANT_FALSE : VARIANT_TRUE; error: RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::put_MultiValued( THIS_ VARIANT_BOOL fMultiValued ) { if ( !_fNTDS ) RRETURN_EXP_IF_ERR( E_NOTIMPL ); HRESULT hr = put_VARIANT_BOOL_Property( (IADs *) this, TEXT("isSingleValued"), !fMultiValued ); if ( hr == E_ADS_CANT_CONVERT_DATATYPE ) { _fNTDS = FALSE; hr = E_NOTIMPL; } RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPProperty::Qualifiers(THIS_ IADsCollection FAR* FAR* ppQualifiers) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } HRESULT CLDAPProperty::AllocatePropertyObject( CCredentials& Credentials, CLDAPProperty FAR * FAR * ppProperty ) { CLDAPProperty FAR *pProperty = NULL; CAggregatorDispMgr FAR *pDispMgr = NULL; CPropertyCache FAR *pPropertyCache = NULL; HRESULT hr = S_OK; pProperty = new CLDAPProperty(); if ( pProperty == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); pDispMgr = new CAggregatorDispMgr(Credentials); if ( pDispMgr == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADs, (IADs *) pProperty, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADsProperty, (IADsProperty *) pProperty, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = CPropertyCache::createpropertycache( (CCoreADsObject FAR *) pProperty, (IGetAttributeSyntax *) pProperty, &pPropertyCache ); BAIL_ON_FAILURE(hr); pDispMgr->RegisterPropertyCache(pPropertyCache); pProperty->_Credentials = Credentials; pProperty->_pDispMgr = pDispMgr; pProperty->_pPropertyCache = pPropertyCache; *ppProperty = pProperty; RRETURN(hr); error: delete pDispMgr; delete pProperty; RRETURN(hr); } // // Needed for dynamic dispid's in the property cache. // HRESULT CLDAPProperty::GetAttributeSyntax( LPWSTR szPropertyName, PDWORD pdwSyntaxId ) { HRESULT hr; hr = LdapGetSyntaxOfAttributeOnServer( _pszLDAPServer, szPropertyName, pdwSyntaxId, _Credentials, _dwPort ); RRETURN_EXP_IF_ERR(hr); } /******************************************************************/ /* Class CLDAPSyntax /******************************************************************/ DEFINE_IDispatch_Implementation(CLDAPSyntax) DEFINE_IADs_Implementation(CLDAPSyntax) DEFINE_IADsPutGet_UnImplementation(CLDAPSyntax) CLDAPSyntax::CLDAPSyntax() : _pDispMgr( NULL ), _pPropertyCache(NULL) { ENLIST_TRACKING(CLDAPSyntax); } CLDAPSyntax::~CLDAPSyntax() { delete _pDispMgr; delete _pPropertyCache; } HRESULT CLDAPSyntax::CreateSyntax( BSTR bstrParent, SYNTAXINFO *pSyntaxInfo, CCredentials& Credentials, DWORD dwObjectState, REFIID riid, void **ppvObj ) { CLDAPSyntax FAR *pSyntax = NULL; HRESULT hr = S_OK; hr = AllocateSyntaxObject(Credentials, &pSyntax ); BAIL_ON_FAILURE(hr); hr = pSyntax->InitializeCoreObject( bstrParent, pSyntaxInfo->pszName, SYNTAX_CLASS_NAME, CLSID_LDAPSyntax, dwObjectState ); BAIL_ON_FAILURE(hr); pSyntax->_lOleAutoDataType = pSyntaxInfo->lOleAutoDataType; // // If the call is from umi we need to instantiate the umi object. // if (Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) { hr = ((CCoreADsObject*)pSyntax)->InitUmiObject( IntfPropsSchema, pSyntax->_pPropertyCache, (IADs *) pSyntax, (IADs *) pSyntax, riid, ppvObj, &(pSyntax->_Credentials) ); BAIL_ON_FAILURE(hr); // // Set the simulated Name property. // hr = HelperPutStringPropertyInCache( L"Name", pSyntaxInfo->pszName, pSyntax->_Credentials, pSyntax->_pPropertyCache ); BAIL_ON_FAILURE(hr); RRETURN(S_OK); } hr = pSyntax->QueryInterface( riid, ppvObj ); BAIL_ON_FAILURE(hr); pSyntax->Release(); RRETURN(hr); error: *ppvObj = NULL; delete pSyntax; RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSyntax::QueryInterface(REFIID iid, LPVOID FAR* ppv) { if (ppv == NULL) { RRETURN(E_POINTER); } if (IsEqualIID(iid, IID_IUnknown)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_IDispatch)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_ISupportErrorInfo)) { *ppv = (ISupportErrorInfo FAR *) this; } else if (IsEqualIID(iid, IID_IADs)) { *ppv = (IADs FAR *) this; } else if (IsEqualIID(iid, IID_IADsSyntax)) { *ppv = (IADsSyntax FAR *) this; } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return NOERROR; } /* ISupportErrorInfo method */ STDMETHODIMP CLDAPSyntax::InterfaceSupportsErrorInfo(THIS_ REFIID riid) { if (IsEqualIID(riid, IID_IADs) || IsEqualIID(riid, IID_IADsSyntax)) { RRETURN(S_OK); } else { RRETURN(S_FALSE); } } /* IADs methods */ STDMETHODIMP CLDAPSyntax::SetInfo(THIS) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } STDMETHODIMP CLDAPSyntax::GetInfo(THIS) { RRETURN(S_OK); } STDMETHODIMP CLDAPSyntax::GetInfoEx(THIS_ VARIANT vProperties, long lnReserved) { RRETURN_EXP_IF_ERR(E_NOTIMPL); } HRESULT CLDAPSyntax::AllocateSyntaxObject( CCredentials& Credentials, CLDAPSyntax FAR * FAR * ppSyntax ) { CLDAPSyntax FAR *pSyntax = NULL; CAggregatorDispMgr FAR *pDispMgr = NULL; CPropertyCache FAR *pPropertyCache = NULL; HRESULT hr = S_OK; pSyntax = new CLDAPSyntax(); if ( pSyntax == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); pDispMgr = new CAggregatorDispMgr(Credentials); if ( pDispMgr == NULL ) hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); hr = pDispMgr->LoadTypeInfoEntry( LIBID_ADs, IID_IADsSyntax, (IADsSyntax *) pSyntax, DISPID_REGULAR ); BAIL_ON_FAILURE(hr); hr = CPropertyCache::createpropertycache( (CCoreADsObject FAR *) pSyntax, (IGetAttributeSyntax *) pSyntax, &pPropertyCache ); BAIL_ON_FAILURE(hr); pSyntax->_pPropertyCache = pPropertyCache; pSyntax->_Credentials = Credentials; pSyntax->_pDispMgr = pDispMgr; *ppSyntax = pSyntax; RRETURN(hr); error: delete pDispMgr; delete pSyntax; delete pPropertyCache; RRETURN_EXP_IF_ERR(hr); } STDMETHODIMP CLDAPSyntax::get_OleAutoDataType( THIS_ long FAR *plOleAutoDataType ) { if ( !plOleAutoDataType ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); *plOleAutoDataType = _lOleAutoDataType; RRETURN(S_OK); } STDMETHODIMP CLDAPSyntax::put_OleAutoDataType( THIS_ long lOleAutoDataType ) { RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_SUPPORTED); } // // Needed for dynamic dispid's in the property cache. // HRESULT CLDAPSyntax::GetAttributeSyntax( LPWSTR szPropertyName, PDWORD pdwSyntaxId ) { HRESULT hr = S_OK; if ((_Credentials.GetAuthFlags() & ADS_AUTH_RESERVED) && !_wcsicmp(L"Name", szPropertyName)) { *pdwSyntaxId = LDAPTYPE_CASEIGNORESTRING; } else { hr = E_ADS_PROPERTY_NOT_FOUND; } RRETURN_EXP_IF_ERR(hr); } /******************************************************************/ /* Misc Helpers /******************************************************************/ HRESULT MakeVariantFromStringArray( BSTR *bstrList, VARIANT *pvVariant ) { HRESULT hr = S_OK; SAFEARRAY *aList = NULL; SAFEARRAYBOUND aBound; if ( (bstrList != NULL) && (*bstrList != 0) ) { long i = 0; long j = 0; long nCount = 0; while ( bstrList[nCount] ) nCount++; if ( nCount == 1 ) { VariantInit( pvVariant ); V_VT(pvVariant) = VT_BSTR; hr = ADsAllocString( bstrList[0], &(V_BSTR(pvVariant))); RRETURN_EXP_IF_ERR(hr); } aBound.lLbound = 0; aBound.cElements = nCount; aList = SafeArrayCreate( VT_VARIANT, 1, &aBound ); if ( aList == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } i = 0; while ( bstrList[i] ) { VARIANT v; VariantInit(&v); V_VT(&v) = VT_BSTR; hr = ADsAllocString( bstrList[i], &(V_BSTR(&v))); BAIL_ON_FAILURE(hr); hr = SafeArrayPutElement( aList, &i, &v ); VariantClear(&v); BAIL_ON_FAILURE(hr); i++; } VariantInit( pvVariant ); V_VT(pvVariant) = VT_ARRAY | VT_VARIANT; V_ARRAY(pvVariant) = aList; } else { aBound.lLbound = 0; aBound.cElements = 0; aList = SafeArrayCreate( VT_VARIANT, 1, &aBound ); if ( aList == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } VariantInit( pvVariant ); V_VT(pvVariant) = VT_ARRAY | VT_VARIANT; V_ARRAY(pvVariant) = aList; } return S_OK; error: if ( aList ) SafeArrayDestroy( aList ); return hr; } HRESULT MakeVariantFromPropStringTable( int *propList, LDAP_SCHEMA_HANDLE hSchema, VARIANT *pvVariant ) { HRESULT hr = S_OK; DWORD nCount = 0; BSTR *aStrings = NULL; if ( propList != NULL ) { while ( propList[nCount] != -1 ) nCount++; } if ( nCount > 0 ) { hr = SchemaGetStringsFromStringTable( hSchema, propList, nCount, &aStrings ); if (FAILED(hr)) RRETURN_EXP_IF_ERR(hr); } hr = MakeVariantFromStringArray( aStrings, pvVariant ); for ( DWORD i = 0; i < nCount; i ++ ) { FreeADsStr( aStrings[i] ); } if (aStrings) { FreeADsMem( aStrings ); } RRETURN(hr); } /* No longer needed HRESULT DeleteSchemaEntry( LPTSTR szADsPath, LPTSTR szRelativeName, LPTSTR szClassName, LPTSTR szSubSchemaSubEntry, CCredentials& Credentials ) { HRESULT hr = S_OK; ADS_LDP *ld = NULL; TCHAR *pszParentLDAPServer = NULL; LPWSTR pszParentLDAPDn = NULL; DWORD dwPort = 0; LPWSTR pszLDAPDn = NULL; LPTSTR *aValues = NULL; int nCount = 0; // // Need to distinguish between LDAP Display Name and ... // // // Get the LDAP server name // hr = BuildLDAPPathFromADsPath2( szADsPath, &pszParentLDAPServer, &pszParentLDAPDn, &dwPort ); BAIL_ON_FAILURE(hr); if ( szSubSchemaSubEntry == NULL ) // not NTDS { hr = E_NOTIMPL; BAIL_ON_FAILURE(hr); } // // Get the name of the schema object // pszLDAPDn = (LPTSTR) AllocADsMem((_tcslen(szRelativeName ) + _tcslen( _tcschr(szSubSchemaSubEntry,TEXT(','))) ) * sizeof(TCHAR)); // includes "\\" if ( pszLDAPDn == NULL ){ hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } _tcscpy( pszLDAPDn, szRelativeName ); _tcscat( pszLDAPDn, _tcschr( szSubSchemaSubEntry, TEXT(',')) ); if ( aValues ) { LdapValueFree( aValues ); aValues = NULL; nCount = 0; } // // Validate the class name first // hr = LdapReadAttribute( pszParentLDAPServer, pszLDAPDn, TEXT("objectClass"), &aValues, &nCount, Credentials, dwPort ); BAIL_ON_FAILURE(hr); if ( nCount > 0 ) { if ( _tcsicmp( szClassName, GET_BASE_CLASS( aValues, nCount) ) != 0 ) { hr = E_ADS_BAD_PARAMETER; BAIL_ON_FAILURE(hr); } } // // Class name has been verified, so delete the object // hr = LdapDeleteS( ld, pszLDAPDn ); BAIL_ON_FAILURE(hr); error: if (pszParentLDAPServer) { FreeADsStr(pszParentLDAPServer); } if (pszParentLDAPDn) { FreeADsStr(pszParentLDAPDn); } if (pszLDAPDn) { FreeADsStr(pszLDAPDn); } if ( aValues ) { LdapValueFree( aValues ); } if ( ld ) { LdapCloseObject( ld ); } RRETURN(hr); } */ // // ******** Important usage note ********** // Users of this function must make sure that cn is part of // the list of attributes passed in. This is a requirement and // the array must contain a NULL string as the last element. // ******** Important usage note ********** // HRESULT BuildSchemaLDAPPathAndGetAttribute( IN LPTSTR pszParent, IN LPTSTR pszName, IN LPTSTR pszSubSchemaSubEntry, IN BOOL fNew, IN CCredentials& Credentials, IN LPTSTR pszAttribs[], OUT LPWSTR * ppszSchemaLDAPServer, OUT LPWSTR * ppszSchemaLDAPDn, IN OUT PADS_LDP *ppLd, // optional in, OUT PLDAPMessage *ppRes // caller need to get first entry ) { HRESULT hr = S_OK; LPTSTR pszLDAPServer = NULL; LPWSTR pszLDAPDn = NULL; DWORD dwPort = 0; BOOL fOpenLd = FALSE; LPTSTR pszSchemaRoot = NULL; LPTSTR pszFilter = NULL; // name on ldap svr LDAPMessage *pE = NULL; int nCount = 0; LPTSTR pszClassName = NULL; LPTSTR *aValues = NULL; int nNumberOfEntries = 0; if ( !ppszSchemaLDAPServer || !ppszSchemaLDAPDn || !ppLd || !ppRes) { RRETURN(E_ADS_BAD_PARAMETER); } // // Using pszSubSchemaSubEntry to test NTDS is no longer accurate. // But the following codes are written to work for NTDS. // if ( pszSubSchemaSubEntry == NULL ) // not NTDS { hr = E_NOTIMPL; BAIL_IF_ERROR(hr); } // // Get the server name & port # // hr = BuildLDAPPathFromADsPath2( pszParent, &pszLDAPServer, &pszLDAPDn, &dwPort ); BAIL_IF_ERROR(hr); // // Connect and bind to schema Root object (in NTDS only) // pszSchemaRoot = _tcschr( // strip CN=Aggregate pszSubSchemaSubEntry, TEXT(',') ); if ( *ppLd == NULL ) { hr = LdapOpenObject( pszLDAPServer, pszSchemaRoot+1, // go past ",", we've stripped CN=Aggregate ppLd, Credentials, dwPort ); BAIL_IF_ERROR(hr); fOpenLd = TRUE; } // // Set Serach Filter to (& (lDAPDisplayName=) // (! (isDefunct=TRUE) ) // ) // pszFilter = (LPTSTR) AllocADsMem((_tcslen(BEGIN_FILTER) + _tcslen(pszName) + _tcslen(END_FILTER) + 1)*sizeof(TCHAR)); if(!pszFilter) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } _tcscpy( pszFilter, BEGIN_FILTER); _tcscat( pszFilter, pszName ); _tcscat( pszFilter, END_FILTER ); // // Search for scheam pszName (class object) under schema root // hr = LdapSearchS( *ppLd, pszSchemaRoot+1, // go past ",", we've stripped CN=Aggregate LDAP_SCOPE_ONELEVEL, pszFilter, pszAttribs, 0, ppRes ); // // Confirm with anoopa & johnsona (ntds5) : // If 1 out of the 2 attributes asked for is not on the svr, // LdapSearchS (ldap_search_s) returns the 1 located and hr = S_OK // BAIL_IF_ERROR(hr); // // Only one active entry should be returned. // If more than one entry is returned, return E_ADS_SCHEMA_VIOLATION // Get cn to build schemalLDAPDn // nNumberOfEntries = LdapCountEntries( *ppLd, *ppRes ); if ( nNumberOfEntries != 1 ) BAIL_IF_ERROR(hr = E_ADS_SCHEMA_VIOLATION); if ( fNew) // ? still keep this { pszClassName = pszName; } else { hr = LdapFirstEntry( *ppLd, *ppRes, &pE ); BAIL_IF_ERROR(hr); hr = LdapGetValues( *ppLd, pE, L"cn", &aValues, &nCount ); BAIL_IF_ERROR(hr); if (nCount == 0) { // use lDAPDisplayName as common name (cn) if cn not set on svr pszClassName = pszName; } else { pszClassName = aValues[0]; } } if (pszLDAPServer!=NULL) { *ppszSchemaLDAPServer = (LPWSTR) AllocADsStr( pszLDAPServer ); if (*ppszSchemaLDAPServer == NULL) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } } else // pszLDAPServer allowed to be NULL { *ppszSchemaLDAPServer = NULL; } *ppszSchemaLDAPDn = (LPWSTR) AllocADsMem( (_tcslen(L"CN=") + _tcslen(pszClassName) + _tcslen(pszSchemaRoot) + 1 ) * sizeof(TCHAR) ); if ( *ppszSchemaLDAPDn == NULL ) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } _tcscpy( *ppszSchemaLDAPDn, L"CN="); _tcscat( *ppszSchemaLDAPDn, pszClassName ); _tcscat( *ppszSchemaLDAPDn, pszSchemaRoot ); // // clean up for both success and failure // release: if ( pszLDAPServer ) FreeADsStr( pszLDAPServer ); if (pszLDAPDn) { FreeADsMem( pszLDAPDn ); } if ( aValues ) LdapValueFree( aValues ); if(pszFilter) { FreeADsMem(pszFilter); pszFilter = NULL; } RRETURN(hr); cleanup: // // clean up if failure only // if (fOpenLd==TRUE) { LdapCloseObject(*ppLd); *ppLd= NULL; } if (*ppRes) { LdapMsgFree(*ppRes); *ppRes=NULL; } if (*ppszSchemaLDAPServer) { FreeADsStr(*ppszSchemaLDAPServer); *ppszSchemaLDAPServer=NULL; } if (*ppszSchemaLDAPDn) { FreeADsMem(*ppszSchemaLDAPDn); *ppszSchemaLDAPDn=NULL; } goto release; } HRESULT BuildSchemaLDAPPath( LPTSTR pszParent, LPTSTR pszName, LPTSTR pszSubSchemaSubEntry, LPWSTR * ppszSchemaLDAPServer, LPWSTR * ppszSchemaLDAPDn, BOOL fNew, ADS_LDP **pld, CCredentials& Credentials ) { HRESULT hr = S_OK; LPTSTR *aValues = NULL; LPTSTR *aValues2 = NULL; int nCount = 0; TCHAR* pszFilter = NULL; LPTSTR aStrings[2]; LDAPMessage *res = NULL; LDAPMessage *e = NULL; LPTSTR pszLDAPServer = NULL; LPWSTR pszLDAPDn = NULL; DWORD dwPort = 0; LPTSTR pszSchemaRoot = NULL; LPTSTR pszClassName = NULL; // // Get the server name // hr = BuildLDAPPathFromADsPath2( pszParent, &pszLDAPServer, &pszLDAPDn, &dwPort ); BAIL_IF_ERROR(hr); if ( pszSubSchemaSubEntry == NULL ) // not NTDS { hr = E_NOTIMPL; BAIL_IF_ERROR(hr); } // the _tcschr is to get rid of "CN=Aggregate" pszSchemaRoot = _tcschr(pszSubSchemaSubEntry, TEXT(',')); if ( fNew ) { pszClassName = pszName; } else { pszFilter = (LPTSTR) AllocADsMem((_tcslen(TEXT("lDAPDisplayName=")) + _tcslen(pszName) + 1)*sizeof(TCHAR)); if(!pszFilter) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } _tcscpy(pszFilter, TEXT("lDAPDisplayName=")); _tcscat( pszFilter, pszName ); aStrings[0] = TEXT("cn"); aStrings[1] = NULL; if ( *pld == NULL ) { hr = LdapOpenObject( pszLDAPServer, pszSchemaRoot + 1, // go past the , - we've stripped off "CN=Aggregate" pld, Credentials, dwPort ); BAIL_IF_ERROR(hr); } hr = LdapSearchS( *pld, pszSchemaRoot + 1, LDAP_SCOPE_ONELEVEL, pszFilter, aStrings, 0, &res ); // Only one entry should be returned if (FAILED(hr) || (FAILED(hr = LdapFirstEntry( *pld, res, &e ))) || (FAILED(hr = LdapGetValues( *pld, e, aStrings[0], &aValues2, &nCount))) ) { BAIL_IF_ERROR(hr); } if ( nCount == 0 ) pszClassName = pszName; else pszClassName = aValues2[0]; } *ppszSchemaLDAPServer = (LPWSTR)AllocADsStr(pszLDAPServer); // // pszLDAPServer might be NULL, in which case NULL is the // expected return value from the alloc. // if ( (*ppszSchemaLDAPServer == NULL) && pszLDAPServer) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } *ppszSchemaLDAPDn = (LPTSTR) AllocADsMem( (_tcslen(L"CN=") + _tcslen(pszClassName) + _tcslen(pszSchemaRoot) + 1 ) * sizeof(TCHAR)); if ( *ppszSchemaLDAPDn == NULL ) { hr = E_OUTOFMEMORY; BAIL_IF_ERROR(hr); } _tcscpy( *ppszSchemaLDAPDn, L"CN="); _tcscat( *ppszSchemaLDAPDn, pszClassName ); _tcscat( *ppszSchemaLDAPDn, pszSchemaRoot ); cleanup: if ( aValues ) LdapValueFree( aValues ); if ( aValues2 ) LdapValueFree( aValues2 ); if ( pszLDAPServer ) FreeADsStr( pszLDAPServer ); if (pszLDAPDn) { FreeADsStr( pszLDAPDn); } if ( res ) LdapMsgFree( res ); if(pszFilter) { FreeADsMem(pszFilter); pszFilter = NULL; } RRETURN(hr); } HRESULT MakePropArrayFromVariant( VARIANT vProp, SCHEMAINFO *hSchema, int **pOIDs, DWORD *pnNumOfOids ) { HRESULT hr = S_OK; int nIndex; LONG dwSLBound; LONG dwSUBound; LONG i = 0; LONG j, k; DWORD nCurrent = 0; *pOIDs = NULL; *pnNumOfOids = 0; if ( !V_ISARRAY( &vProp)) { // special case of one object (not an array) nIndex = FindSearchTableIndex( V_BSTR(&vProp), hSchema->aPropertiesSearchTable, hSchema->nNumOfProperties * 2 ); if ( nIndex != -1 ) { *pOIDs = (int *) AllocADsMem( sizeof(int) * 2); if ( *pOIDs == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } (*pOIDs)[nCurrent++] = nIndex; (*pOIDs)[nCurrent] = -1; *pnNumOfOids = 1; } else { hr = E_ADS_PROPERTY_NOT_FOUND; } RRETURN_EXP_IF_ERR(hr); } // // Here, we have an array of properties. We want to create an array of // indexes into the aPropertiesSearchTable // hr = SafeArrayGetLBound(V_ARRAY(&vProp), 1, (long FAR *)&dwSLBound ); BAIL_ON_FAILURE(hr); hr = SafeArrayGetUBound(V_ARRAY(&vProp), 1, (long FAR *)&dwSUBound ); BAIL_ON_FAILURE(hr); *pOIDs = (int *) AllocADsMem( sizeof(int) * (dwSUBound - dwSLBound + 2)); if ( *pOIDs == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } for (i = dwSLBound; i <= dwSUBound; i++) { VARIANT v; VariantInit(&v); hr = SafeArrayGetElement(V_ARRAY(&vProp), (long FAR *)&i, &v ); BAIL_ON_FAILURE(hr); nIndex = FindSearchTableIndex( V_BSTR(&v), hSchema->aPropertiesSearchTable, hSchema->nNumOfProperties * 2 ); VariantClear(&v); if ( nIndex != -1 ) { (*pOIDs)[nCurrent++] = nIndex; } else { hr = E_ADS_PROPERTY_NOT_FOUND; BAIL_ON_FAILURE(hr); } } (*pOIDs)[nCurrent] = -1; *pnNumOfOids = nCurrent; SortAndRemoveDuplicateOIDs( *pOIDs, pnNumOfOids ); error: if (FAILED(hr)) { if ( *pOIDs ) { FreeADsMem( *pOIDs ); *pOIDs = NULL; } } RRETURN(hr); } HRESULT MakePropArrayFromStringArray( LPTSTR *aValues, DWORD nCount, SCHEMAINFO *hSchema, int **pOIDs, DWORD *pnNumOfOids ) { HRESULT hr = S_OK; int nIndex; DWORD i = 0; DWORD nCurrent = 0; *pOIDs = NULL; *pnNumOfOids = 0; // // Here, we have an array of properties. We want to create an array of // indexes into the aPropertiesSearchTable // *pOIDs = (int *) AllocADsMem( sizeof(int) * (nCount+1)); if ( *pOIDs == NULL ) { hr = E_OUTOFMEMORY; BAIL_ON_FAILURE(hr); } for (i = 0; i < nCount ; i++) { nIndex = FindSearchTableIndex( aValues[i], hSchema->aPropertiesSearchTable, hSchema->nNumOfProperties * 2 ); if ( nIndex != -1 ) { (*pOIDs)[nCurrent++] = nIndex; } else { hr = E_ADS_PROPERTY_NOT_FOUND; BAIL_ON_FAILURE(hr); } } (*pOIDs)[nCurrent] = -1; *pnNumOfOids = nCurrent; qsort( *pOIDs, *pnNumOfOids, sizeof((*pOIDs)[0]), intcmp ); error: if (FAILED(hr)) { if ( *pOIDs ) { FreeADsMem( *pOIDs ); *pOIDs = NULL; } } RRETURN(hr); } /******************************************************************/ /* Misc Schema functions /******************************************************************/ BOOL GetLdapClassPrimaryInterface( LPTSTR pszLdapClass, GUID **ppPrimaryInterfaceGUID, GUID **ppCLSID ) { for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ ) { if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 ) { *ppPrimaryInterfaceGUID = (GUID *) aClassMap[i].pPrimaryInterfaceGUID; *ppCLSID = (GUID *) aClassMap[i].pCLSID; return TRUE; } } return FALSE; } BOOL GetPrimaryInterface( LPTSTR pszClassName, SCHEMAINFO *pSchemaInfo, PCLASSNAME_LIST pClassNames, GUID **ppPrimaryInterfaceGUID, GUID **ppCLSID ) { int i = 0; CLASSINFO *pClassInfo; LPTSTR pszName; DWORD index; PCLASSNAME_LIST pClass = NULL; PCLASSNAME_LIST pNextClass = NULL; BOOL fExitStatus = FALSE; if ( GetLdapClassPrimaryInterface( pszClassName, ppPrimaryInterfaceGUID, ppCLSID )) { return TRUE; } index = (DWORD) FindEntryInSearchTable( pszClassName, pSchemaInfo->aClassesSearchTable, 2 * pSchemaInfo->nNumOfClasses ); if ( index == ((DWORD) -1) ) return FALSE; // // Recursively search the list of superiors and // aux classes. To avoid loops, we maintain a list // of classes we have already reached. If we are called // with a class on this list, we abort. // // // Make sure the current class isn't already on the list // if (pClassNames) { for (pNextClass = pClassNames; pNextClass != NULL; pNextClass = pNextClass->pNext) { if (_tcscmp(pNextClass->pszClassName, pszClassName) == 0) { // found match, bail fExitStatus = FALSE; BAIL_ON_SUCCESS(S_OK); } } } // // Construct a node for the current class & add it to the list // pClass = static_cast(AllocADsMem(sizeof(CLASSNAME_LIST))); if (!pClass) { BAIL_ON_FAILURE(E_OUTOFMEMORY); } pClass->pszClassName = static_cast(AllocADsMem((_tcslen(pszClassName)+1) * sizeof(TCHAR))); if (!pClass->pszClassName) { BAIL_ON_FAILURE(E_OUTOFMEMORY); } _tcscpy(pClass->pszClassName, pszClassName); pClass->pNext = pClassNames; // // Perform the recursive search // pClassInfo = &(pSchemaInfo->aClasses[index]); if ( pClassInfo->pOIDsSuperiorClasses ) { for ( i = 0; (pszName = pClassInfo->pOIDsSuperiorClasses[i]); i++ ) { if ( GetPrimaryInterface( pszName, pSchemaInfo, pClass, ppPrimaryInterfaceGUID, ppCLSID )) { fExitStatus = TRUE; BAIL_ON_SUCCESS(S_OK); } } } if ( pClassInfo->pOIDsAuxClasses ) { for ( i = 0; (pszName = pClassInfo->pOIDsAuxClasses[i]); i++ ) { if ( GetPrimaryInterface( pszName, pSchemaInfo, pClass, ppPrimaryInterfaceGUID, ppCLSID )) { fExitStatus = TRUE; BAIL_ON_SUCCESS(S_OK); } } } error: // // Each level of recursion is responsible for freeing // its own corresponding node. // if (pClass) { if (pClass->pszClassName) { FreeADsMem(pClass->pszClassName); } FreeADsMem(pClass); } return fExitStatus; } HRESULT SchemaGetPrimaryInterface( LDAP_SCHEMA_HANDLE hSchema, LPTSTR pszClassName, GUID **ppPrimaryInterfaceGUID, GUID **ppCLSID ) { HRESULT hr = S_OK; SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema; if ( !pSchemaInfo ) RRETURN_EXP_IF_ERR(E_ADS_BAD_PARAMETER); GetPrimaryInterface( pszClassName, pSchemaInfo, NULL, ppPrimaryInterfaceGUID, ppCLSID ); RRETURN(hr); } BOOL MapLdapClassToADsClass( LPTSTR *aLdapClasses, int nCount, LPTSTR pszADsClass ) { *pszADsClass = 0; if ( nCount == 0 ) return FALSE; if ( _tcsicmp( aLdapClasses[nCount-1], TEXT("Top")) == 0 ) { for ( int j = 0; j < nCount; j++ ) { LPTSTR pszLdapClass = aLdapClasses[j]; for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ ) { if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 ) { _tcscpy( pszADsClass, aClassMap[i].pszADsClassName ); return TRUE; } } } _tcscpy( pszADsClass, aLdapClasses[0] ); return FALSE; } else { for ( int j = nCount-1; j >= 0; j-- ) { LPTSTR pszLdapClass = aLdapClasses[j]; for ( int i = 0; i < ARRAY_SIZE(aClassMap); i++ ) { if ( _tcsicmp( pszLdapClass, aClassMap[i].pszLdapClassName ) == 0 ) { _tcscpy( pszADsClass, aClassMap[i].pszADsClassName ); return TRUE; } } } _tcscpy( pszADsClass, aLdapClasses[nCount-1] ); return FALSE; } } BOOL MapLdapClassToADsClass( LPTSTR pszClassName, LDAP_SCHEMA_HANDLE hSchema, LPTSTR pszADsClass ) { LPTSTR aClasses[1]; CLASSINFO *pClassInfo = NULL; SCHEMAINFO *pSchemaInfo = (SCHEMAINFO *) hSchema; *pszADsClass = 0; aClasses[0] = pszClassName; if ( MapLdapClassToADsClass( aClasses, 1, pszADsClass )) return TRUE; DWORD index = (DWORD) FindEntryInSearchTable( pszClassName, pSchemaInfo->aClassesSearchTable, 2 * pSchemaInfo->nNumOfClasses ); if ( index == ((DWORD) -1) ) // cannot find the class name in the schema { _tcscpy( pszADsClass, pszClassName ); return FALSE; } pClassInfo = &(pSchemaInfo->aClasses[index]); if ( pClassInfo->pOIDsSuperiorClasses ) { LPTSTR pszName = NULL; for ( int i = 0; (pszName = pClassInfo->pOIDsSuperiorClasses[i]); i++ ) { if ( MapLdapClassToADsClass( pszName, pSchemaInfo, pszADsClass)) return TRUE; } } _tcscpy( pszADsClass, pszClassName ); return FALSE; } LPTSTR MapADsClassToLdapClass( LPTSTR pszADsClass, LPTSTR pszLdapClass ) { for ( int i=0; i < ARRAY_SIZE(aClassMap); i++ ) { if ( _tcsicmp( pszADsClass, aClassMap[i].pszADsClassName ) == 0 ) { _tcscpy( pszLdapClass, aClassMap[i].pszLdapClassName ); return pszLdapClass; } } _tcscpy( pszLdapClass, pszADsClass ); return pszLdapClass; } STDMETHODIMP makeUnionVariantFromLdapObjects( LDAPOBJECTARRAY ldapSrcObjects1, LDAPOBJECTARRAY ldapSrcObjects2, VARIANT FAR * pvPossSuperiors ) { HRESULT hr = S_OK; BSTR *retVals = NULL; DWORD dwNumVals = 0; BSTR curString = NULL; DWORD dwMaxVals = 0; DWORD dwCtr = 0; DWORD dwArrIndx = 0; PLDAPOBJECT pLdapObject; dwMaxVals = ldapSrcObjects1.dwCount + ldapSrcObjects2.dwCount + 1; retVals = (BSTR *)AllocADsMem(dwMaxVals * sizeof(BSTR *)); if (!retVals) { BAIL_ON_FAILURE(hr = E_OUTOFMEMORY); } for (dwCtr = 0; dwCtr < ldapSrcObjects1.dwCount; dwCtr++) { pLdapObject = ldapSrcObjects1.pLdapObjects + dwCtr; curString = LDAPOBJECT_STRING(pLdapObject); hr = addStringIfAbsent(curString, retVals, &dwArrIndx); BAIL_ON_FAILURE(hr); } for (dwCtr = 0; dwCtr < ldapSrcObjects2.dwCount; dwCtr++) { pLdapObject = ldapSrcObjects2.pLdapObjects + dwCtr; curString = LDAPOBJECT_STRING(pLdapObject); hr = addStringIfAbsent(curString, retVals, &dwArrIndx); BAIL_ON_FAILURE(hr); } // do the same for the second ldapobjectarray hr = MakeVariantFromStringArray(retVals, pvPossSuperiors); error: // clean up the string array either way for (dwCtr=0; dwCtr < dwArrIndx; dwCtr++) { ADsFreeString(retVals[dwCtr]); } FreeADsMem(retVals); RRETURN(hr); } STDMETHODIMP addStringIfAbsent( BSTR addString, BSTR *strArray, PDWORD dwArrIndx ) { HRESULT hr = S_OK; DWORD dwCtr = 0; BOOLEAN fFound = FALSE; for (dwCtr = 0; (dwCtr < *dwArrIndx) && !fFound; dwCtr ++) { if (!_wcsicmp(addString, strArray[dwCtr])) { fFound = TRUE; } } if (!fFound) { hr = ADsAllocString( addString, &strArray[*dwArrIndx] ); BAIL_ON_FAILURE(hr); (*dwArrIndx)++; strArray[*dwArrIndx] = NULL; } error: RRETURN(hr); }