You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
513 lines
12 KiB
513 lines
12 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
|
|
//
|
|
// FILE
|
|
//
|
|
// dsobject.cpp
|
|
//
|
|
// SYNOPSIS
|
|
//
|
|
// This file defines the class DSObject.
|
|
//
|
|
// MODIFICATION HISTORY
|
|
//
|
|
// 02/20/1998 Original version.
|
|
// 06/09/1998 Refresh property cache before enumerating dirty objects.
|
|
// 02/11/1999 Keep downlevel parameters in sync.
|
|
// 03/16/1999 Return error if downlevel update fails.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <ias.h>
|
|
#include <iasutil.h>
|
|
#include <dsenum.h>
|
|
#include <dsobject.h>
|
|
|
|
#include <vector>
|
|
|
|
WCHAR USER_PARAMETERS_NAME[] = L"UserParameters";
|
|
|
|
// Smart pointer for an IADsPropertyEntryPtr.
|
|
_COM_SMARTPTR_TYPEDEF(IADsPropertyEntry, __uuidof(IADsPropertyEntry));
|
|
|
|
//////////
|
|
// Prefix added to all RDN's.
|
|
//////////
|
|
_bstr_t DSObject::thePrefix(L"CN=");
|
|
|
|
//////////
|
|
// The name of the 'name' property.
|
|
//////////
|
|
_bstr_t DSObject::theNameProperty(L"name");
|
|
_bstr_t DSObject::theUserParametersProperty(USER_PARAMETERS_NAME);
|
|
|
|
|
|
DSObject::DSObject(IUnknown* subject)
|
|
: oldParms(NULL)
|
|
{
|
|
_Module.Lock();
|
|
// All subjects must support IADs && IDirectoryObject.
|
|
_com_util::CheckError(subject->QueryInterface(
|
|
__uuidof(IADs),
|
|
(PVOID*)&leaf
|
|
));
|
|
|
|
_com_util::CheckError(Restore());
|
|
}
|
|
|
|
DSObject::~DSObject() throw ()
|
|
{
|
|
SysFreeString(oldParms);
|
|
_Module.Unlock();
|
|
}
|
|
|
|
//////////
|
|
// IUnknown implementation is copied from CComObject<>.
|
|
//////////
|
|
|
|
STDMETHODIMP_(ULONG) DSObject::AddRef()
|
|
{
|
|
return InternalAddRef();
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) DSObject::Release()
|
|
{
|
|
ULONG l = InternalRelease();
|
|
|
|
if (l == 0) { delete this; }
|
|
|
|
return l;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::QueryInterface(REFIID iid, void ** ppvObject)
|
|
{
|
|
return _InternalQueryInterface(iid, ppvObject);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_Name(BSTR* pVal)
|
|
{
|
|
if (pVal == NULL) { return E_OUTOFMEMORY; }
|
|
|
|
VARIANT v;
|
|
|
|
RETURN_ERROR(leaf->Get(theNameProperty, &v));
|
|
|
|
// We should have gotten back a non-null BSTR.
|
|
if (V_VT(&v) != VT_BSTR || V_BSTR(&v) == NULL) { return E_FAIL; }
|
|
|
|
*pVal = V_BSTR(&v);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_Class(BSTR* pVal)
|
|
{
|
|
return leaf->get_Class(pVal);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_GUID(BSTR* pVal)
|
|
{
|
|
return leaf->get_GUID(pVal);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_Container(IDataStoreContainer** pVal)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::GetValue(BSTR bstrName, VARIANT* pVal)
|
|
{
|
|
return leaf->Get(bstrName, pVal);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::GetValueEx(BSTR bstrName, VARIANT* pVal)
|
|
{
|
|
return leaf->GetEx(bstrName, pVal);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::PutValue(BSTR bstrName, VARIANT* pVal)
|
|
{
|
|
if (pVal == NULL) { return E_INVALIDARG; }
|
|
|
|
// Flag the object as dirty.
|
|
dirty = TRUE;
|
|
|
|
// Synch up the downlevel parameters.
|
|
downlevel.PutValue(bstrName, pVal);
|
|
|
|
if ( VT_EMPTY == V_VT(pVal) )
|
|
{
|
|
return leaf->PutEx(ADS_PROPERTY_CLEAR, bstrName, *pVal);
|
|
}
|
|
else if ( VT_ARRAY == (V_VT(pVal) & VT_ARRAY) )
|
|
{
|
|
return leaf->PutEx(ADS_PROPERTY_UPDATE, bstrName, *pVal);
|
|
}
|
|
else
|
|
{
|
|
return leaf->Put(bstrName, *pVal);
|
|
}
|
|
}
|
|
|
|
STDMETHODIMP DSObject::Update()
|
|
{
|
|
// Update the UserParameters.
|
|
PWSTR newParms;
|
|
HRESULT hr = downlevel.Update(oldParms, &newParms);
|
|
if (FAILED(hr)) { return hr; }
|
|
|
|
// Convert to a VARIANT.
|
|
VARIANT value;
|
|
VariantInit(&value);
|
|
V_VT(&value) = VT_BSTR;
|
|
V_BSTR(&value) = SysAllocString(newParms);
|
|
|
|
// Set the UserParameters property.
|
|
leaf->Put(theUserParametersProperty, value);
|
|
|
|
// Clean-up.
|
|
VariantClear(&value);
|
|
LocalFree(newParms);
|
|
|
|
return leaf->SetInfo();
|
|
}
|
|
|
|
STDMETHODIMP DSObject::Restore()
|
|
{
|
|
// Free the old UserParameters.
|
|
if (oldParms)
|
|
{
|
|
SysFreeString(oldParms);
|
|
oldParms = NULL;
|
|
}
|
|
|
|
dirty = FALSE;
|
|
HRESULT hr = leaf->GetInfo();
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Read the UserParameters property.
|
|
VARIANT value;
|
|
if (leaf->Get(theUserParametersProperty, &value) == S_OK)
|
|
{
|
|
if (V_VT(&value) == VT_BSTR)
|
|
{
|
|
oldParms = V_BSTR(&value);
|
|
}
|
|
else
|
|
{
|
|
// This should never happen.
|
|
VariantClear(&value);
|
|
}
|
|
}
|
|
}
|
|
else if (hr == E_ADS_OBJECT_UNBOUND)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::Item(BSTR bstrName, IDataStoreProperty** pVal)
|
|
{
|
|
if (bstrName == NULL || pVal == NULL) { return E_INVALIDARG; }
|
|
|
|
*pVal = NULL;
|
|
|
|
// Get the value for this item.
|
|
_variant_t value;
|
|
RETURN_ERROR(leaf->Get(bstrName, &value));
|
|
|
|
try
|
|
{
|
|
// Create a new property object.
|
|
(*pVal = new MyProperty(bstrName, value, this))->AddRef();
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_PropertyCount(long* pVal)
|
|
{
|
|
if (pVal == NULL) { return E_INVALIDARG; }
|
|
|
|
if (dirty)
|
|
{
|
|
RETURN_ERROR(Restore());
|
|
}
|
|
|
|
MyProperties properties(leaf);
|
|
|
|
if (!properties)
|
|
{
|
|
// Some ADSI providers may not implement IADsPropertyList.
|
|
*pVal = 0;
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
return properties->get_PropertyCount(pVal);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_NewPropertyEnum(IUnknown** pVal)
|
|
{
|
|
if (pVal == NULL) { return E_INVALIDARG; }
|
|
|
|
*pVal = NULL;
|
|
|
|
if (dirty)
|
|
{
|
|
RETURN_ERROR(Restore());
|
|
}
|
|
|
|
MyProperties properties(leaf);
|
|
|
|
// Some ADSI providers may not implement IADsPropertyList.
|
|
if (!properties) { return E_NOTIMPL; }
|
|
|
|
// Reset the list in case this isn't the first time we've enumerated it.
|
|
properties->Reset();
|
|
|
|
try
|
|
{
|
|
using _com_util::CheckError;
|
|
|
|
// How many properties are there?
|
|
long count;
|
|
CheckError(properties->get_PropertyCount(&count));
|
|
|
|
// Create a temporary array of items.
|
|
std::vector<_variant_t> items;
|
|
items.reserve(count);
|
|
|
|
//////////
|
|
// Load all the properties into the temporary array.
|
|
//////////
|
|
|
|
while (count--)
|
|
{
|
|
// Get the next item in the list.
|
|
_variant_t item;
|
|
CheckError(properties->Next(&item));
|
|
|
|
// Convert it to a Property Entry.
|
|
IADsPropertyEntryPtr entry(item);
|
|
|
|
// Get the property name.
|
|
BSTR bstrName;
|
|
CheckError(entry->get_Name(&bstrName));
|
|
_bstr_t name(bstrName, false);
|
|
|
|
// Get the property value.
|
|
_variant_t value;
|
|
HRESULT hr = leaf->Get(name, &value);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (hr == E_ADS_CANT_CONVERT_DATATYPE)
|
|
{
|
|
// This must be one of those nasty NTDS attributes that has
|
|
// no VARIANT representation.
|
|
continue;
|
|
}
|
|
|
|
_com_issue_error(hr);
|
|
}
|
|
|
|
// Create the property object and add it to the vector.
|
|
items.push_back(new MyProperty(name, value, this));
|
|
}
|
|
|
|
//////////
|
|
// Create and initialize an enumerator for the items.
|
|
//////////
|
|
|
|
CComPtr<EnumVARIANT> newEnum(new CComObject<EnumVARIANT>);
|
|
|
|
_com_util::CheckError(newEnum->Init(items.begin(),
|
|
items.end(),
|
|
NULL,
|
|
AtlFlagCopy));
|
|
|
|
// Return it to the caller.
|
|
(*pVal = newEnum)->AddRef();
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::Item(BSTR bstrName, IDataStoreObject** ppObject)
|
|
{
|
|
if (ppObject == NULL) { return E_INVALIDARG; }
|
|
|
|
try
|
|
{
|
|
// Get the ADSI object.
|
|
CComPtr<IDispatch> disp;
|
|
_com_util::CheckError(node->GetObject(NULL,
|
|
thePrefix + bstrName,
|
|
&disp));
|
|
|
|
// Convert to a DSObject.
|
|
*ppObject = spawn(disp);
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::Create(BSTR bstrClass,
|
|
BSTR bstrName,
|
|
IDataStoreObject** ppObject)
|
|
{
|
|
if (ppObject == NULL) { return E_INVALIDARG; }
|
|
|
|
try
|
|
{
|
|
// Create the ADSI object.
|
|
CComPtr<IDispatch> disp;
|
|
_com_util::CheckError(node->Create(bstrClass,
|
|
thePrefix + bstrName,
|
|
&disp));
|
|
|
|
// Convert to a DSObject.
|
|
*ppObject = spawn(disp);
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::MoveHere(IDataStoreObject* pObject,
|
|
BSTR bstrNewName)
|
|
{
|
|
if (pObject == NULL) { return E_INVALIDARG; }
|
|
|
|
try
|
|
{
|
|
using _com_util::CheckError;
|
|
|
|
// Downcast to a DSObject.
|
|
DSObject* obj = DSObject::narrow(pObject);
|
|
|
|
// Get the absolute path of the object being moved.
|
|
CComBSTR path;
|
|
CheckError(obj->leaf->get_ADsPath(&path));
|
|
|
|
// Is the object being renamed?
|
|
_bstr_t newName(bstrNewName ? thePrefix + bstrNewName : _bstr_t());
|
|
|
|
// Move it to this container.
|
|
CComPtr<IDispatch> disp;
|
|
CheckError(node->MoveHere(path, newName, &disp));
|
|
|
|
//////////
|
|
// Set the leaf to the new object.
|
|
//////////
|
|
|
|
CComPtr<IADs> ads;
|
|
CheckError(disp->QueryInterface(__uuidof(IADs), (PVOID*)&ads));
|
|
|
|
obj->leaf.Release();
|
|
obj->leaf = ads;
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::Remove(BSTR bstrClass, BSTR bstrName)
|
|
{
|
|
if (bstrClass == NULL) { return E_INVALIDARG; }
|
|
|
|
try
|
|
{
|
|
_com_util::CheckError(node->Delete(bstrClass, thePrefix + bstrName));
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_ChildCount(long *pVal)
|
|
{
|
|
return node->get_Count(pVal);
|
|
}
|
|
|
|
STDMETHODIMP DSObject::get_NewChildEnum(IUnknown** pVal)
|
|
{
|
|
if (pVal == NULL) { return E_INVALIDARG; }
|
|
|
|
*pVal = NULL;
|
|
|
|
try
|
|
{
|
|
// Get the ADSI enumerator.
|
|
CComPtr<IUnknown> unk;
|
|
_com_util::CheckError(node->get__NewEnum(&unk));
|
|
|
|
// Convert to an IEnumVARIANT.
|
|
CComPtr<IEnumVARIANT> enumVariant;
|
|
_com_util::CheckError(unk->QueryInterface(__uuidof(IEnumVARIANT),
|
|
(PVOID*)&enumVariant));
|
|
|
|
// Construct our wrapper around the real enumerator.
|
|
(*pVal = new DSEnumerator(this, enumVariant))->AddRef();
|
|
}
|
|
CATCH_AND_RETURN()
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
IDataStoreObject* DSObject::spawn(IUnknown* subject)
|
|
{
|
|
DSObject* child = new DSObject(subject);
|
|
|
|
child->InternalAddRef();
|
|
|
|
return child;
|
|
}
|
|
|
|
|
|
DSObject* DSObject::narrow(IUnknown* p)
|
|
{
|
|
DSObject* object;
|
|
|
|
using _com_util::CheckError;
|
|
|
|
CheckError(p->QueryInterface(__uuidof(DSObject), (PVOID*)&object));
|
|
|
|
// We can get away with InternalRelease since the caller must still
|
|
// have a reference to this object.
|
|
object->InternalRelease();
|
|
|
|
return object;
|
|
}
|
|
|
|
HRESULT WINAPI DSObject::getContainer(void* pv, REFIID, LPVOID* ppv, DWORD_PTR)
|
|
{
|
|
DSObject* obj = (DSObject*)pv;
|
|
|
|
// If we don't have a node pointer, try to get one.
|
|
if (obj->node == NULL)
|
|
{
|
|
obj->leaf->QueryInterface(__uuidof(IADsContainer), (PVOID*)&obj->node);
|
|
}
|
|
|
|
// If node is not NULL, then we are a container.
|
|
if (obj->node != NULL)
|
|
{
|
|
*ppv = (IDataStoreContainer*)obj;
|
|
|
|
obj->AddRef();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
*ppv = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
}
|