//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1995.
//
//  File:       cprops.cxx
//
//  Contents:   Property Cache functionality for NT
//
//  Functions:
//                CPropertyCache::addproperty
//                CPropertyCache::updateproperty
//                CPropertyCache::findproperty
//                CPropertyCache::getproperty
//                CPropertyCache::putproperty
//                CProperyCache::CPropertyCache
//                CPropertyCache::~CPropertyCache
//                CPropertyCache::createpropertycache
//
//  History:      17-June-1996   RamV   Created.
//                cloned off NT property cache code
//
//
//----------------------------------------------------------------------------

#include "winnt.hxx"
#pragma  hdrstop
#define INITGUID

#if DBG
DECLARE_INFOLEVEL(NTMarshall);
DECLARE_DEBUG(NTMarshall);
#define NTMarshallDebugOut(x) NTMarshallInlineDebugOut x
#endif



void
ADsECodesToDispECodes(
    HRESULT *pHr
    );


//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::addproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --
//              [vt]                --
//              [vaData]            --
//
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
addproperty(
    LPWSTR szPropertyName,
    DWORD  dwSyntaxId,
    DWORD  dwNumValues,
    PNTOBJECT pNtObject
    )
{
    HRESULT hr = S_OK;
    PPROPERTY pNewProperty = NULL;
    LPWSTR tempString = NULL;

    //
    // Allocate the string first
    //
    tempString = AllocADsStr(szPropertyName);

    if (!tempString)
       BAIL_ON_FAILURE(hr=E_OUTOFMEMORY);

    //
    //  extend the property cache by adding a new property entry
    //

    _pProperties = (PPROPERTY)ReallocADsMem(
                                _pProperties,
                                _cb,
                                _cb + sizeof(PROPERTY)
                                );
    if (!_pProperties) {
        hr = E_OUTOFMEMORY;
        BAIL_ON_FAILURE(hr);
    }


    pNewProperty = (PPROPERTY)((LPBYTE)_pProperties + _cb);

    if (pNewProperty->szPropertyName) {
       FreeADsStr(pNewProperty->szPropertyName);
       pNewProperty->szPropertyName = NULL;
    }

    pNewProperty->szPropertyName = tempString;

    //
    // Update the index
    //

    _dwMaxProperties++;
    _cb += sizeof(PROPERTY);


    //
    // add to dynamic dispatch table now ???
    //  - don't check schema here, is it more efficient at all? inconsistency
    //    ???
    //
    /*
    hr = DispatchAddProperty(
            szPropertyName
            );
    BAIL_ON_FAILURE(hr);
    */


    RRETURN(hr);

error:

    if (tempString)
       FreeADsStr(tempString);

    RRETURN_EXP_IF_ERR(hr);
}






//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::updateproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --
//              [vaData]    --
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::updateproperty(
    LPWSTR szPropertyName,
    DWORD  dwSyntaxId,
    DWORD  dwNumValues,
    PNTOBJECT pNtObject,
    BOOL fExplicit
    )
{
    HRESULT hr;
    DWORD dwIndex;
    PNTOBJECT pNtTempObject = NULL;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;


    if (!fExplicit) {
        if (PROPERTY_FLAGS(pThisProperty) & CACHE_PROPERTY_MODIFIED) {
            hr = S_OK;
            goto error;
        }
    }

    //
    //   Factor in cases where object state is necessary to
    //   decide on update.
    //

    if (PROPERTY_NTOBJECT(pThisProperty)) {

        NTTypeFreeNTObjects(
                PROPERTY_NTOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty)
                );
        PROPERTY_NTOBJECT(pThisProperty) = NULL;
    }

    PROPERTY_SYNTAX(pThisProperty) = dwSyntaxId;
    PROPERTY_NUMVALUES(pThisProperty) = dwNumValues;

    hr = NtTypeCopyConstruct(
            pNtObject,
            dwNumValues,
            &pNtTempObject
            );
    BAIL_ON_FAILURE(hr);

    PROPERTY_NTOBJECT(pThisProperty) = pNtTempObject;

    PROPERTY_FLAGS(pThisProperty)  &= ~PROPERTY_MODIFIED;

error:

    RRETURN_EXP_IF_ERR(hr);

}


//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::findproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName] --
//              [pdwIndex]       --
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::findproperty(
    LPWSTR szPropertyName,
    PDWORD pdwIndex
    )

{
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;

    for (i = 0; i < _dwMaxProperties; i++) {

        pThisProperty = _pProperties + i;

        if (!_wcsicmp(pThisProperty->szPropertyName, szPropertyName)) {
            *pdwIndex = i;
            RRETURN(S_OK);
        }
    }
    *pdwIndex = 0;
    RRETURN(E_ADS_PROPERTY_NOT_FOUND);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::getproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Property to retrieve from the cache
//              [pvaData]           --  Data returned in a variant
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
getproperty(
    LPWSTR szPropertyName,
    PDWORD  pdwSyntaxId,
    PDWORD  pdwNumValues,
    PNTOBJECT * ppNtObject,
    BOOL    *pfModified
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PPROPERTY pThisProperty = NULL;
    DWORD dwResult;
    DWORD dwInfoLevel = 0;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );

    // don't do implicit GetInfo if there is no ADSI object backing the
    // property cache. This will be true for UMI interafec properties.
    if ((hr == E_ADS_PROPERTY_NOT_FOUND) && (_pCoreADsObject != NULL)) {

        hr = GetPropertyInfoLevel(
                    szPropertyName,
                    _pSchemaClassProps,
                    _dwNumProperties,
                    &dwInfoLevel
                    );
        BAIL_ON_FAILURE(hr);

        //
        // Now call the GetInfo function
        //

        hr = _pCoreADsObject->GetInfo(
                    dwInfoLevel,
                    FALSE
                    );
        BAIL_ON_FAILURE(hr);

        hr = findproperty(
                    szPropertyName,
                    &dwIndex
                    );

    }
    BAIL_ON_FAILURE(hr);




    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_NTOBJECT(pThisProperty)) {

        *pdwSyntaxId = (DWORD)PROPERTY_SYNTAX(pThisProperty);
        *pdwNumValues = (DWORD)PROPERTY_NUMVALUES(pThisProperty);

        if(pfModified != NULL) { // caller wants to know if prop. was modified
            if(PROPERTY_FLAGS(pThisProperty) & CACHE_PROPERTY_MODIFIED)
                *pfModified = TRUE;
            else
                *pfModified = FALSE;
        } 

        hr = NtTypeCopyConstruct(PROPERTY_NTOBJECT(pThisProperty),
                                 PROPERTY_NUMVALUES(pThisProperty),
                                 ppNtObject );
        BAIL_ON_FAILURE(hr);
    }else {

        *ppNtObject = NULL;
        *pdwNumValues = 0;
        *pdwSyntaxId = 0;
        hr = E_FAIL;

    }

error:
    RRETURN_EXP_IF_ERR(hr);
}


//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::marshallgetproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Property to retrieve from the cache
//              [pvaData]           --  Data returned in a variant
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
marshallgetproperty(
    LPWSTR szPropertyName,
    PDWORD  pdwSyntaxId,
    PDWORD  pdwNumValues,
    PNTOBJECT * ppNtObject
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PPROPERTY pThisProperty = NULL;
    DWORD dwResult;
    DWORD dwInfoLevel = 0;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );

    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;


    //
    // If the data has not changed, then do not
    // return data from the cache
    //
    if (PROPERTY_FLAGS(pThisProperty) == 0) {
        hr = E_ADS_PROPERTY_NOT_MODIFIED;
        BAIL_ON_FAILURE(hr);
    }

    if (PROPERTY_NTOBJECT(pThisProperty)) {

        *pdwSyntaxId = (DWORD)PROPERTY_SYNTAX(pThisProperty);
        *pdwNumValues = (DWORD)PROPERTY_NUMVALUES(pThisProperty);

        hr = NtTypeCopyConstruct(PROPERTY_NTOBJECT(pThisProperty),
                                 PROPERTY_NUMVALUES(pThisProperty),
                                 ppNtObject );
        BAIL_ON_FAILURE(hr);
    }else {

        *ppNtObject = NULL;
        *pdwNumValues = 0;
        *pdwSyntaxId = 0;
        hr = E_FAIL;

    }

error:
    RRETURN_EXP_IF_ERR(hr);
}

//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::putproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Clsid index
//              [vaData]    --  Matching clsid returned in *pclsid
//
//-------------------------------------------------------------------------

HRESULT
CPropertyCache::putproperty(
    LPWSTR szPropertyName,
    DWORD  dwSyntaxId,
    DWORD  dwNumValues,
    PNTOBJECT pNtObject,
    BOOL   fMarkAsClean
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PNTOBJECT pNtTempObject = NULL;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_NTOBJECT(pThisProperty)) {

        NTTypeFreeNTObjects(
                PROPERTY_NTOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty)
                );
        PROPERTY_NTOBJECT(pThisProperty) = NULL;
    }

    PROPERTY_SYNTAX(pThisProperty) = dwSyntaxId;
    PROPERTY_NUMVALUES(pThisProperty) = dwNumValues;

    hr = NtTypeCopyConstruct(
            pNtObject,
            dwNumValues,
            &pNtTempObject
            );
    BAIL_ON_FAILURE(hr);

    PROPERTY_NTOBJECT(pThisProperty) = pNtTempObject;

    if(FALSE == fMarkAsClean)
        PROPERTY_FLAGS(pThisProperty) |= CACHE_PROPERTY_MODIFIED;

error:
    RRETURN_EXP_IF_ERR(hr);
}



//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
CPropertyCache::CPropertyCache():
    _pCoreADsObject(NULL),
    _pSchemaClassProps(NULL),
    _dwMaxProperties(0),
    _dwCurrentIndex(0),
    _pProperties(NULL),
    _cb(0),
    _pDispProperties(NULL),
    _dwDispMaxProperties(0),
    _cbDisp(0)
{

}

//+------------------------------------------------------------------------
//
//  Function:   ~CPropertyCache
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
CPropertyCache::
~CPropertyCache()
{
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;
    PDISPPROPERTY pThisDispProperty = NULL;

    if (_pProperties) {

        for (i = 0; i < _dwMaxProperties; i++) {

            pThisProperty = _pProperties + i;

            if (pThisProperty->szPropertyName) {
               FreeADsStr(pThisProperty->szPropertyName);
               pThisProperty->szPropertyName = NULL;
            }

            if (PROPERTY_NTOBJECT(pThisProperty)) {

                NTTypeFreeNTObjects(
                        PROPERTY_NTOBJECT(pThisProperty),
                        PROPERTY_NUMVALUES(pThisProperty)
                        );
                PROPERTY_NTOBJECT(pThisProperty) = NULL;
            }
        }

        FreeADsMem(_pProperties);
    }


    //
    // Free Dynamic Dispid Table
    //

    if (_pDispProperties) {

        for ( DWORD i = 0; i < _dwDispMaxProperties; i++ ) {

            pThisDispProperty = _pDispProperties + i;

            if (pThisDispProperty->szPropertyName) {
               FreeADsStr(pThisDispProperty->szPropertyName);
               pThisDispProperty->szPropertyName = NULL;
            }

        }

        FreeADsMem(_pDispProperties);
    }


}

//+------------------------------------------------------------------------
//
//  Function:
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
createpropertycache(
    PPROPERTYINFO pSchemaClassProps,
    DWORD dwNumProperties,
    CCoreADsObject FAR * pCoreADsObject,
    CPropertyCache FAR *FAR * ppPropertyCache
    )
{
    CPropertyCache FAR * pPropertyCache = NULL;

    pPropertyCache = new CPropertyCache();

    if (!pPropertyCache) {
        RRETURN_EXP_IF_ERR(E_OUTOFMEMORY);
    }

    pPropertyCache->_pCoreADsObject = pCoreADsObject;
    pPropertyCache->_pSchemaClassProps = pSchemaClassProps;
    pPropertyCache->_dwNumProperties = dwNumProperties;

    *ppPropertyCache = pPropertyCache;

    RRETURN(S_OK);
}


//+------------------------------------------------------------------------
//
//  Function:
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------

HRESULT
CPropertyCache::
unmarshallproperty(
    LPWSTR szPropertyName,
    LPBYTE lpValue,
    DWORD  dwNumValues,
    DWORD  dwSyntaxId,
    BOOL fExplicit
    )
{

    DWORD dwIndex = 0;
    HRESULT hr = S_OK;
    PNTOBJECT pNTObject = NULL;

    hr = UnMarshallNTToNTSynId(
                dwSyntaxId,
                dwNumValues,
                lpValue,
                &pNTObject
                );
    BAIL_ON_FAILURE(hr);

    //
    // Find this property in the cache
    //

    hr = findproperty(
                szPropertyName,
                &dwIndex
                );

    //
    // If this property does not exist in the
    // cache, add this property into the cache.
    //


    if (FAILED(hr)) {
        hr = addproperty(
                    szPropertyName,
                    dwSyntaxId,
                    dwNumValues,
                    pNTObject
                    );

        //
        // If the operation fails for some reason
        // move on to the next property
        //
        BAIL_ON_FAILURE(hr);

    }

    //
    // Now update the property in the cache
    //

    hr = updateproperty(
                    szPropertyName,
                    dwSyntaxId,
                    dwNumValues,
                    pNTObject,
                    fExplicit
                    );
    BAIL_ON_FAILURE(hr);

error:

    if (pNTObject) {
        NTTypeFreeNTObjects(
                pNTObject,
                dwNumValues
                );

    }

    RRETURN_EXP_IF_ERR(hr);
}



HRESULT
ValidatePropertyinSchemaClass(
    PPROPERTYINFO pSchemaClassProps,
    DWORD dwNumProperties,
    LPWSTR pszPropName,
    PDWORD pdwSyntaxId
    )
{
    DWORD i = 0;

    PPROPERTYINFO pThisSchProperty = NULL;

    for (i = 0; i < dwNumProperties; i++) {

         pThisSchProperty =  (pSchemaClassProps + i);

        if (!_wcsicmp(pszPropName, pThisSchProperty->szPropertyName)) {
            *pdwSyntaxId = pThisSchProperty->dwSyntaxId;
            RRETURN (S_OK);
        }
    }

    RRETURN(E_ADS_SCHEMA_VIOLATION);
}



HRESULT
ValidateIfWriteableProperty(
    PPROPERTYINFO pSchemaClassProps,
    DWORD dwNumProperties,
    LPWSTR pszPropName
    )
{
    DWORD i = 0;

    PPROPERTYINFO pThisSchProperty = NULL;

    for (i = 0; i < dwNumProperties; i++) {

         pThisSchProperty =  (pSchemaClassProps + i);

        if (!_wcsicmp(pszPropName, pThisSchProperty->szPropertyName)) {

             RRETURN((pThisSchProperty->dwFlags & PROPERTY_WRITEABLE)
                        ? S_OK : E_ADS_SCHEMA_VIOLATION);
        }
    }

    RRETURN(E_ADS_SCHEMA_VIOLATION);

    // for winnt & nw312, return E_ADS_SCHEMA_VIOLATION if not ok even
    // if just try to write to cache only
}



HRESULT
GetPropertyInfoLevel(
    LPWSTR pszPropName,
    PPROPERTYINFO pSchemaClassProps,
    DWORD dwNumProperties,
    PDWORD pdwInfoLevel
    )
{
    DWORD i = 0;

    PPROPERTYINFO pThisSchProperty = NULL;

    for (i = 0; i < dwNumProperties; i++) {

         pThisSchProperty =  (pSchemaClassProps + i);

        if (!_wcsicmp(pszPropName, pThisSchProperty->szPropertyName)) {

             *pdwInfoLevel = pThisSchProperty->dwInfoLevel;
             RRETURN(S_OK);
        }
    }

    //
    // Returning E_ADS_PROPERTY_NOT_FOUND so that implicit
    // GetInfo fails gracefully
    //
    RRETURN(E_ADS_PROPERTY_NOT_FOUND);
}


//+------------------------------------------------------------------------
//
//  Function:   ~CPropertyCache
//
//  Synopsis:
//
//
//
//  Arguments:
//
//
//-------------------------------------------------------------------------
void
CPropertyCache::
flushpropcache()
{
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;

    if (_pProperties) {

        for (i = 0; i < _dwMaxProperties; i++) {

            pThisProperty = _pProperties + i;

            if (pThisProperty->szPropertyName) {
               FreeADsStr(pThisProperty->szPropertyName);
               pThisProperty->szPropertyName = NULL;
            }

            if (PROPERTY_NTOBJECT(pThisProperty)) {

                NTTypeFreeNTObjects(
                        PROPERTY_NTOBJECT(pThisProperty),
                        PROPERTY_NUMVALUES(pThisProperty)
                        );
                PROPERTY_NTOBJECT(pThisProperty) = NULL;
            }
        }

        FreeADsMem(_pProperties);
    }

    //
    // Reset the property cache
    //

    _pProperties = NULL;
    _dwMaxProperties = 0;
    _cb = 0;
}




//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::getproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Property to retrieve from the cache
//              [pvaData]           --  Data returned in a variant
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
unboundgetproperty(
    LPWSTR szPropertyName,
    PDWORD  pdwSyntaxId,
    PDWORD  pdwNumValues,
    PNTOBJECT * ppNtObject
    )
{
    HRESULT hr;
    DWORD dwIndex = 0L;
    PPROPERTY pThisProperty = NULL;

    hr = findproperty(
            szPropertyName,
            &dwIndex
            );
    BAIL_ON_FAILURE(hr);

    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_NTOBJECT(pThisProperty)) {

        *pdwSyntaxId = (DWORD)PROPERTY_SYNTAX(pThisProperty);
        *pdwNumValues = (DWORD)PROPERTY_NUMVALUES(pThisProperty);

        hr = NtTypeCopyConstruct(
                PROPERTY_NTOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty),
                ppNtObject
                );
        BAIL_ON_FAILURE(hr);

    }else {

        *ppNtObject = NULL;
        *pdwNumValues = 0;
        *pdwSyntaxId = 0;
        hr = E_FAIL;

    }

error:

   RRETURN_EXP_IF_ERR(hr);
}
//+------------------------------------------------------------------------
//
//  Function:   CPropertyCache::getproperty
//
//  Synopsis:
//
//
//
//  Arguments:  [szPropertyName]    --  Property to retrieve from the cache
//              [pvaData]           --  Data returned in a variant
//
//-------------------------------------------------------------------------
HRESULT
CPropertyCache::
unboundgetproperty(
    DWORD dwIndex,
    PDWORD  pdwSyntaxId,
    PDWORD  pdwNumValues,
    PNTOBJECT * ppNtObject
    )
{
    HRESULT hr;
    PPROPERTY pThisProperty = NULL;

    if (!index_valid(dwIndex)) {
        RRETURN_EXP_IF_ERR(E_ADS_PROPERTY_NOT_FOUND);
    }

    pThisProperty = _pProperties + dwIndex;

    if (PROPERTY_NTOBJECT(pThisProperty)) {

        *pdwSyntaxId = (DWORD)PROPERTY_SYNTAX(pThisProperty);
        *pdwNumValues = (DWORD)PROPERTY_NUMVALUES(pThisProperty);

        hr = NtTypeCopyConstruct(
                PROPERTY_NTOBJECT(pThisProperty),
                PROPERTY_NUMVALUES(pThisProperty),
                ppNtObject
                );
        BAIL_ON_FAILURE(hr);

    }else {

        *ppNtObject = NULL;
        *pdwNumValues = 0;
        *pdwSyntaxId = 0;
        hr = E_FAIL;

    }

error:

   RRETURN_EXP_IF_ERR(hr);
}


BOOL
CPropertyCache::
index_valid(
   )
{
    //
    // need to check _dwMaxProperties==0 separately since a negative
    // DWORD is equal to +ve large #
    //

    if (_dwMaxProperties==0 || (_dwCurrentIndex>_dwMaxProperties-1) )
        return(FALSE);
    else
        return(TRUE);
}


BOOL
CPropertyCache::
index_valid(
   DWORD dwIndex
   )
{
    //
    // need to check _dwMaxProperties==0 separately since a negative
    // DWORD is equal to +ve large #
    //

    if (_dwMaxProperties==0 || (dwIndex>_dwMaxProperties-1) )
        return(FALSE);
    else
        return(TRUE);
}


void
CPropertyCache::
reset_propindex(
    )
{
  _dwCurrentIndex = 0;

}


HRESULT
CPropertyCache::
skip_propindex(
    DWORD dwElements
    )
{
    DWORD newIndex = _dwCurrentIndex + dwElements;

    if (!index_valid())
        RRETURN_EXP_IF_ERR(E_FAIL);

    //
    // - allow current index to go from within range to out of range by 1
    // - by 1 since initial state is out of range by 1
    //

    if (newIndex>_dwMaxProperties) {
        RRETURN_EXP_IF_ERR(E_FAIL);
    }

    _dwCurrentIndex = newIndex;

    RRETURN(S_OK);

}


HRESULT
CPropertyCache::
get_PropertyCount(
    PDWORD pdwMaxProperties
    )
{
    *pdwMaxProperties = _dwMaxProperties;

    RRETURN(S_OK);
}

DWORD
CPropertyCache::
get_CurrentIndex(
    )
{
    return(_dwCurrentIndex);
}


LPWSTR
CPropertyCache::
get_CurrentPropName(
    )

{
    PPROPERTY pThisProperty = NULL;

    if (!_dwMaxProperties)      // if !_dwMaxProperties, pThisProperty=NULL, AV
        return NULL;            // in PROPERTY_NAME(pThisProperty)

    if (!index_valid())
        return(NULL);

    pThisProperty = _pProperties + _dwCurrentIndex;

    return(PROPERTY_NAME(pThisProperty));
}

LPWSTR
CPropertyCache::
get_PropName(
    DWORD dwIndex
    )

{
    PPROPERTY pThisProperty = NULL;

    if (!index_valid(dwIndex))
       return(NULL);

    pThisProperty = _pProperties + dwIndex;

    return(PROPERTY_NAME(pThisProperty));
}


HRESULT
CPropertyCache::
deleteproperty(
    DWORD dwIndex
    )
{
   HRESULT hr = S_OK;
   PPROPERTY pNewProperties = NULL;
   PPROPERTY pThisProperty = _pProperties + dwIndex;

   if (!index_valid(dwIndex)) {
      hr = E_FAIL;
      BAIL_ON_FAILURE(hr);
   }

   if (_dwMaxProperties == 1) {
      //
      // Deleting everything
      //
      if (PROPERTY_NTOBJECT(pThisProperty)) {
          NTTypeFreeNTObjects(
                  PROPERTY_NTOBJECT(pThisProperty),
                  PROPERTY_NUMVALUES(pThisProperty)
                  );
          PROPERTY_NTOBJECT(pThisProperty) = NULL;
      }

      FreeADsMem(_pProperties);
      _pProperties = NULL;
      _dwMaxProperties = 0;
      _cb = 0;
      //
      // Reset the current index just in case
      //
      _dwCurrentIndex = 0;
      RRETURN(hr);
   }

   pNewProperties = (PPROPERTY)AllocADsMem(
                               _cb - sizeof(PROPERTY)
                               );
   if (!pNewProperties) {
       hr = E_OUTOFMEMORY;
       BAIL_ON_FAILURE(hr);
   }

   //
   // Copying the memory before the deleted item
   //
   if (dwIndex != 0) {
      memcpy( pNewProperties,
              _pProperties,
              dwIndex * sizeof(PROPERTY));
   }

   //
   // Copying the memory following the deleted item
   //
   if (dwIndex != (_dwMaxProperties-1)) {
      memcpy( pNewProperties + dwIndex,
              _pProperties + dwIndex + 1,
              (_dwMaxProperties - dwIndex - 1) * sizeof(PROPERTY));
   }

   if (PROPERTY_NTOBJECT(pThisProperty)) {
       NTTypeFreeNTObjects(
               PROPERTY_NTOBJECT(pThisProperty),
               PROPERTY_NUMVALUES(pThisProperty)
               );
       PROPERTY_NTOBJECT(pThisProperty) = NULL;
   }
   FreeADsMem(_pProperties);
   _pProperties = pNewProperties;
   _dwMaxProperties--;
   _cb -= sizeof(PROPERTY);
   //
   // Reset the current index if necesary so we do not skip a property.
   //
   if (_dwCurrentIndex > dwIndex) {
       _dwCurrentIndex--;
   }
error:

   RRETURN_EXP_IF_ERR(hr);
}


////////////////////////////////////////////////////////////////////////
//
//  IPropertyCache
//

HRESULT
CPropertyCache::
locateproperty(
    LPWSTR  szPropertyName,
    PDWORD  pdwDispid
    )
{
    HRESULT hr;

    hr = DispatchLocateProperty(
            szPropertyName,
            pdwDispid
            );

    RRETURN(hr);
}

HRESULT
CPropertyCache::
putproperty(
    DWORD   dwDispid,
    VARIANT varValue
    )
{
    HRESULT hr;

    hr = DispatchPutProperty(
            dwDispid,
            varValue
            );

    RRETURN(hr);
}

HRESULT
CPropertyCache::
getproperty(
    DWORD   dwDispid,
    VARIANT * pvarValue
    )
{
    HRESULT hr;

    hr = DispatchGetProperty(
            dwDispid,
            pvarValue
            );

    RRETURN(hr);
}


////////////////////////////////////////////////////////////////////////
//
// Dynamic Dispid Table
//

HRESULT
CPropertyCache::
DispatchFindProperty(
    LPWSTR szPropertyName,
    PDWORD pdwDispid
    )
{
    DWORD i = 0;
    PDISPPROPERTY pDispProp = NULL;

    //
    // use ADs Error codes since this funct'n does not go directly into
    // the dispatch interface
    //
    if (!pdwDispid || !szPropertyName)
        RRETURN(E_ADS_BAD_PARAMETER);

    for (i=0; i<_dwDispMaxProperties; i++) {

        pDispProp = _pDispProperties + i;

        if (!_wcsicmp(DISPATCH_NAME(pDispProp), szPropertyName)) {
            *pdwDispid=i;
            RRETURN(S_OK);
        }
    }

    *pdwDispid = (DWORD) -1;
    RRETURN(E_ADS_PROPERTY_NOT_FOUND);
}

HRESULT
CPropertyCache::
DispatchAddProperty(
    LPWSTR szPropertyName,
    PDWORD pdwDispid    /* optional */
    )
{

    HRESULT hr = E_FAIL;
    DWORD dwDispid = (DWORD) -1;
    PDISPPROPERTY pNewDispProps = NULL;
    LPWSTR pszTempName = NULL;

    //
    // use ADs Error codes since this funct'n does not go directly into
    // the dispatch interface
    //
    if (!szPropertyName)
        RRETURN(E_ADS_BAD_PARAMETER);

    hr = DispatchFindProperty(
            szPropertyName,
            &dwDispid
            );

    if (hr==E_ADS_PROPERTY_NOT_FOUND) {

        pszTempName = AllocADsStr(szPropertyName);
        if (!pszTempName)
            BAIL_ON_FAILURE(hr=E_OUTOFMEMORY);

        //
        // increase the size of Dynamic Dispid Table by 1 property
        //
        pNewDispProps = (PDISPPROPERTY) ReallocADsMem(
                                            _pDispProperties,
                                            _cbDisp,
                                            _cbDisp + sizeof(DISPPROPERTY)
                                            );
        if (!pNewDispProps)
            BAIL_ON_FAILURE(hr=E_OUTOFMEMORY);

        //
        // must succeeded at this pt. okay to change table & indexes
        //
        _pDispProperties = pNewDispProps;
        DISPATCH_NAME(_pDispProperties+_dwDispMaxProperties) = pszTempName;
        dwDispid = _dwDispMaxProperties++;
        _cbDisp += sizeof(DISPPROPERTY);

        hr = S_OK;
    }

    //
    // return valid, or invalid (-1) in case of failure, dispid of
    // szProperty iff asked for
    //
    if (pdwDispid)
        *pdwDispid = dwDispid;

    RRETURN(hr);


error:

    if (pszTempName)
        FreeADsStr(pszTempName);

    RRETURN(hr);
}

HRESULT
CPropertyCache::
DispatchLocateProperty(
    LPWSTR szPropertyName,
    PDWORD pdwDispid
    )
{
    HRESULT hr;
    DWORD dwSyntaxId;   // (dummy)

    //
    // - pdwDispid not optional here
    // - Use DISP_E_ERROR codes since this function directly called by
    //   the dispatch manager
    //
    if (!pdwDispid || !szPropertyName)
        RRETURN(DISP_E_PARAMNOTOPTIONAL);

    //
    // return dispid of property if already in table;
    //
    hr = DispatchFindProperty(
            szPropertyName,
            pdwDispid
            );

    if (hr==E_ADS_PROPERTY_NOT_FOUND) {

        //
        // check if property in schema
        //      - this is necessary; otherwise, property not in schema will
        //        be allowed to be added to cache and will not be given the
        //        chance to be handled by 3rd party extension.
        //      - note that property not in schema but added to the cache
        //        thru' IADsProperty list will not be handled by 3rd
        //        party extension either.
        //
        hr = ValidatePropertyinSchemaClass(
                _pSchemaClassProps,
                _dwNumProperties,
                szPropertyName,
                &dwSyntaxId
                );

        //
        // Add property that is in the schema but not in the cache to
        // the dynamic dispid table. That is, property which is in the
        // schema will always be handled by the cache/server thur ADSI but
        // will NOT be handled by 3rd party extension.
        //
        if (SUCCEEDED(hr)) {

            hr = DispatchAddProperty(
                        szPropertyName,
                        pdwDispid
                        );
            BAIL_ON_FAILURE(hr);

        }

        //
        // Property Not in the schema will nto be added to the dynamic
        // dispid table and could be handled by 3rd party extension.
        //
        else {

            hr = DISP_E_MEMBERNOTFOUND;
            BAIL_ON_FAILURE(hr);

        }
    }

    RRETURN(hr);

error:

    //
    // translate E_ADS_ error codes to DISP_E if appropriate, see above
    //
    ADsECodesToDispECodes(&hr);

    *pdwDispid = (DWORD) DISPID_UNKNOWN;

    RRETURN(hr);
}


HRESULT
CPropertyCache::
DispatchGetProperty(
    DWORD dwDispid,
    VARIANT * pvarVal
    )
{
    HRESULT hr;
    LPWSTR szPropName = NULL;
    DWORD dwSyntaxId = (DWORD) -1;
    DWORD dwNumValues = 0;
    PNTOBJECT pNtObjs = NULL;

    //
    // Use DISP_E_ERROR codes since this function directly called by
    // the dispatch manager
    //
    if (!pvarVal)
        RRETURN(DISP_E_PARAMNOTOPTIONAL);

    if (!DISPATCH_INDEX_VALID(dwDispid))
        RRETURN(DISP_E_MEMBERNOTFOUND);

    szPropName = DISPATCH_PROPERTY_NAME(dwDispid);

    //
    // return value in cache for szPropName; retrieve value from server
    // if not already in cache; fail if none on sever
    //
    hr = getproperty(
            szPropName,
            &dwSyntaxId,
            &dwNumValues,
            &pNtObjs
            );
    BAIL_ON_FAILURE(hr);

    //
    // translate NT objects into variants
    //
    if (dwNumValues == 1) {

        hr = NtTypeToVarTypeCopy(
                pNtObjs,
                pvarVal
                );

    } else {

        hr = NtTypeToVarTypeCopyConstruct(
                pNtObjs,
                dwNumValues,
                pvarVal
                );
    }
    BAIL_ON_FAILURE(hr);

error:

    if (pNtObjs) {

        NTTypeFreeNTObjects(
            pNtObjs,
            dwNumValues
            );
    }

    if (FAILED(hr)) {

        //
        // return DISP_E errors instead E_ADS_ errors , see above
        //
        ADsECodesToDispECodes(&hr);

        V_VT(pvarVal) = VT_ERROR;
    }

    RRETURN(hr);
}

HRESULT
CPropertyCache::
DispatchPutProperty(
    DWORD dwDispid,
    VARIANT& varVal
    )
{
    HRESULT hr;
    LPWSTR szPropName = NULL;
    VARIANT * pvProp = NULL;            // do not free
    DWORD dwNumValues = 0;
    VARIANT * pTempVarArray = NULL;     // to be freed
    DWORD dwSyntaxId = (DWORD) -1;
    LPNTOBJECT pNtObjs = NULL;
    DWORD dwIndex = (DWORD) -1;


    //
    // Use DISP_E_ERROR codes since this function directly called by
    // the dispatch manager
    //
    if (!DISPATCH_INDEX_VALID(dwDispid))
        RRETURN(DISP_E_MEMBERNOTFOUND);

    //
    // retreive property name from Dynamic Dispatch Table
    //
    szPropName = DISPATCH_PROPERTY_NAME(dwDispid);


    //
    // translate variant to NT Objects
    //

    //
    // 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 = &varVal;

    if (V_VT(pvProp) == (VT_BYREF|VT_VARIANT)) {
        pvProp = V_VARIANTREF(pvProp);
    }

    if ((V_VT(pvProp) == (VT_VARIANT|VT_ARRAY)) ||
        (V_VT(pvProp) == (VT_VARIANT|VT_ARRAY|VT_BYREF))) {

        hr  = ConvertByRefSafeArrayToVariantArray(
                    varVal,
                    &pTempVarArray,
                    &dwNumValues
                    );
        BAIL_ON_FAILURE(hr);

        if(NULL == pTempVarArray) {
            BAIL_ON_FAILURE(hr = E_INVALIDARG);
        }        

        pvProp = pTempVarArray;

    }else {

        //
        // Single value NOT stored in array MUST BE ALLOWED since clients
        // would expect Put() to behave the same whether the dipatch
        // manager is invoked or not. (This funct'n has to be consitent
        // GenericPutPropertyManager(), but NOT GenericPutExProperty...)

        dwNumValues = 1;
    }

    //
    // Need the syntax of this property on the cache.
    //
    hr = ValidatePropertyinSchemaClass(
            _pSchemaClassProps,
            _dwNumProperties,
            szPropName,
            &dwSyntaxId
            );

    BAIL_ON_FAILURE(hr);

    //
    // check if this is a writeable property in schema
    //

    hr = ValidateIfWriteableProperty(
                _pSchemaClassProps,
                _dwNumProperties,
                szPropName
                );
    BAIL_ON_FAILURE(hr);

    //
    // Variant Array to Nt Objects
    //
    hr = VarTypeToNtTypeCopyConstruct(
                    dwSyntaxId,
                    pvProp,
                    dwNumValues,
                    &pNtObjs
                    );
    BAIL_ON_FAILURE(hr);


    //
    // add the property to cache if not already in since DispatchAddProperty
    // does not addproperty
    //
    hr = findproperty(
                szPropName,
                &dwIndex
                );

    if (FAILED(hr)) {
        hr = addproperty(
                    szPropName,
                    dwSyntaxId,
                    dwNumValues,
                    pNtObjs
                    );
        BAIL_ON_FAILURE(hr);
    }

    //
    // update property value in cache
    //
    hr = putproperty(
                szPropName,
                dwSyntaxId,
                dwNumValues,
                pNtObjs
                );
    BAIL_ON_FAILURE(hr);


error:

    if (pNtObjs) {
        NTTypeFreeNTObjects(
            pNtObjs,
            dwNumValues
            );
    }

    if (pTempVarArray) {

        DWORD i = 0;
        for (i = 0; i < dwNumValues; i++) {
            VariantClear(pTempVarArray + i);
        }
        FreeADsMem(pTempVarArray);
    }


    if (FAILED(hr)) {

        //
        // return DISP_E errors instead E_ADS_ errors , see above
        //
        ADsECodesToDispECodes(&hr);
    }

    RRETURN(hr);
}

//----------------------------------------------------------------------------
// Function:   GetPropNames
//
// Synopsis:   Returns the names of all properties in the cache.
//
// Arguments:
//
// pProps      Returns the names of the properties, without any data
//
// Returns:    UMI_S_NO_ERROR on success. Error code otherwise.
//
// Modifies:   *pProps to return the property names
//
//----------------------------------------------------------------------------
HRESULT CPropertyCache::GetPropNames(
    UMI_PROPERTY_VALUES **pProps
    )
{
    UMI_PROPERTY_VALUES *pUmiPropVals = NULL;
    UMI_PROPERTY        *pUmiProps = NULL;
    HRESULT             hr = UMI_S_NO_ERROR;
    ULONG               ulIndex = 0;
    PPROPERTY           pNextProperty = NULL;

    ADsAssert(pProps != NULL);

    pUmiPropVals = (UMI_PROPERTY_VALUES *) AllocADsMem(
                          sizeof(UMI_PROPERTY_VALUES));
    if(NULL == pUmiPropVals)
        BAIL_ON_FAILURE(hr = UMI_E_OUT_OF_MEMORY);

    memset(pUmiPropVals, 0, sizeof(UMI_PROPERTY_VALUES));

    if(0 == _dwMaxProperties) {
    // no properties in cache
        *pProps = pUmiPropVals;
        RRETURN(UMI_S_NO_ERROR);
    }

    pUmiProps = (UMI_PROPERTY *) AllocADsMem(
                          _dwMaxProperties * sizeof(UMI_PROPERTY));
    if(NULL == pUmiProps)
        BAIL_ON_FAILURE(hr = UMI_E_OUT_OF_MEMORY);

    memset(pUmiProps, 0, _dwMaxProperties * sizeof(UMI_PROPERTY));

    for(ulIndex = 0; ulIndex < _dwMaxProperties; ulIndex++) {
        pNextProperty = _pProperties + ulIndex;

        pUmiProps[ulIndex].pszPropertyName =
            (LPWSTR) AllocADsStr(pNextProperty->szPropertyName);
        if(NULL == pUmiProps[ulIndex].pszPropertyName)
            BAIL_ON_FAILURE(hr = UMI_E_OUT_OF_MEMORY);
    }

    pUmiPropVals->uCount = _dwMaxProperties;
    pUmiPropVals->pPropArray = pUmiProps;

    *pProps = pUmiPropVals;

    RRETURN(UMI_S_NO_ERROR);   

error:

    if(pUmiProps != NULL) {
        for(ulIndex = 0; ulIndex < _dwMaxProperties; ulIndex++)
            if(pUmiProps[ulIndex].pszPropertyName != NULL)
                FreeADsStr(pUmiProps[ulIndex].pszPropertyName);

        FreeADsMem(pUmiProps);
    }

    if(pUmiPropVals != NULL)
        FreeADsMem(pUmiPropVals);

    RRETURN(hr);
} 

//----------------------------------------------------------------------------
// Function:   ClearModifiedFlag 
//
// Synopsis:   Clears the modified flag for all properties in the cache. This
//             is done after a successful SetInfo so that subsequent Get
//             operations return the correct state of the property. 
//
// Arguments:
//
// None
//
// Returns:    Nothing 
//
// Modifies:   Nothing 
//
//----------------------------------------------------------------------------
void
CPropertyCache::ClearModifiedFlags(void)
{
    DWORD i = 0;
    PPROPERTY pThisProperty = NULL;

    for (i = 0; i < _dwMaxProperties; i++) {

        pThisProperty = _pProperties + i;

        if (PROPERTY_NTOBJECT(pThisProperty)) 
            PROPERTY_FLAGS(pThisProperty) &= ~CACHE_PROPERTY_MODIFIED;
    }
}


//
// Move This function out of this file, out of adsnt in fact. LATER
// Moving it out may make the conversion more difficult since each
// provider return error codes in its own way. May be local is better.
//
void
ADsECodesToDispECodes(
    HRESULT *pHr
    )
{
    DWORD dwADsErr = *pHr;

    switch (dwADsErr) {

    case E_ADS_UNKNOWN_OBJECT:
    case E_ADS_PROPERTY_NOT_SUPPORTED:
    case E_ADS_PROPERTY_INVALID:
    case E_ADS_PROPERTY_NOT_FOUND:

        *pHr = DISP_E_MEMBERNOTFOUND;
        break;

    case E_ADS_BAD_PARAMETER:

        //*pHr = DISP_E_PARAMNOTOPTIONAL;
        break;

    case E_ADS_CANT_CONVERT_DATATYPE:

        *pHr = DISP_E_TYPEMISMATCH;
        //*pHr = DISP_E_BADVARTYPE;
        break;

    case E_ADS_SCHEMA_VIOLATION:

        // depends
        break;

    default:

        break;
        // should make it s.t. E_ADS_xxx -> E_FAIL and no changes on others
        // LATER
    };

}