//
//  Author: ushaji
//  Date:   December 1996
//
//
//    Providing support for Component Categories in Class Store
//
//      This source file contains implementations for ICatInformation interfaces.                             ¦
//
//      Refer Doc "Design for Support of File Types and Component Categories
//      in Class Store" ? (or may be Class Store Schema)
//


#include "cstore.hxx"

//---------------------------------------------------------------
// EnumCategories:
//      returns the enumerator to enumerate categories.
//        lcid:                    locale id.
//        ppenumcategoryinfo:        Enumerator that is returned.
//
// ppEnumCategoryInfo value is undefined if an error occurs
//---------------------------------------------------------------

HRESULT __stdcall CClassContainer::EnumCategories(LCID lcid, IEnumCATEGORYINFO **ppenumCategoryInfo)
{
    HRESULT                 hr = S_OK;
    CSCEnumCategories      *pEnum = NULL;
    
    if (!m_fOpen)
        return E_FAIL;
    
    if (!IsValidPtrOut(this, sizeof(*this)))
        return E_ACCESSDENIED;
    
    if (!IsValidPtrOut(ppenumCategoryInfo, sizeof(IEnumCATEGORYINFO *)))
        return E_INVALIDARG;
    
    *ppenumCategoryInfo=NULL;
    
    // get the enumerator object.
    pEnum=new CSCEnumCategories;
    if(NULL == pEnum)
        return E_OUTOFMEMORY;
    
    // initialize the enumerator object with the name.
    hr = pEnum->Initialize(m_szCategoryName, lcid);
    ERROR_ON_FAILURE(hr);
    
    hr = pEnum->QueryInterface(IID_IEnumCATEGORYINFO, (void**)ppenumCategoryInfo);
    ERROR_ON_FAILURE(hr);

    return S_OK;
Error_Cleanup:
    if (pEnum)
        delete pEnum;
    
    return RemapErrorCode(hr, m_szContainerName);
} /* EnumCategories */

//---------------------------------------------------------------
// GetCategoryDesc:
//        returns the description of a given category.
//        rcatid:         category id.
//        lcid:           locale id.
//        ppszDesc        pointer to the description string to be returned.
//                        Allocated by the function. to be freed by client.
//--------------------------------------------------------------------------
HRESULT __stdcall CClassContainer::GetCategoryDesc(REFCATID rcatid, LCID lcid, LPOLESTR *ppszDesc)
{
    STRINGGUIDRDN       szRDN;
    ULONG               cdesc = 0, i, cgot = 0;
    LPOLESTR          * localedesc = NULL;
    HANDLE              hADs = NULL;
    HRESULT             hr = S_OK;
    WCHAR             * szFullName = NULL;
    LPOLESTR            AttrName = LOCALEDESCRIPTION;
    ADS_ATTR_INFO     * pAttr = NULL;
    
    if (!IsValidPtrOut(ppszDesc, sizeof(LPOLESTR)))
        return E_INVALIDARG;
    
    if (IsNullGuid(rcatid))
        return E_INVALIDARG;
    
    if (!IsValidPtrOut(this, sizeof(*this)))
        return E_ACCESSDENIED;
    
    RDNFromGUID(rcatid, szRDN);
    
    BuildADsPathFromParent(m_szCategoryName, szRDN, &szFullName);
    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        &hADs);
    
    if (FAILED(hr))
        return CAT_E_CATIDNOEXIST;
    
    // get the array of localized descriptions
    hr = ADSIGetObjectAttributes(hADs, &AttrName, 1, &pAttr, &cgot);
    
    if (FAILED(hr) || (!cgot))
        return CAT_E_NODESCRIPTION;
    
    hr = UnpackStrArrFrom(pAttr[0], &localedesc, &cdesc);
    
    if (hr == E_OUTOFMEMORY)
        return hr;
    
    *ppszDesc = (WCHAR *)CoTaskMemAlloc(sizeof(WCHAR)*128);
    if (!(*ppszDesc))
        return E_OUTOFMEMORY;
    
    // get a description closest to the locale we need.
    GetCategoryLocaleDesc(localedesc, cdesc, &lcid, *ppszDesc);
    
    CoTaskMemFree(localedesc);
    
    ADSICloseDSObject(hADs);
    
    FreeADsMem(pAttr);
    
    FreeADsMem(szFullName);
    
    return S_OK;
    
} /* GetCategoryDesc */


//---------------------------------------------------------------
// EnumClassesOfCategories:
//        returns the enumerator for classes that implements given catids and
//                requires some given catids.
//
//        cImplemented    number of implemented categories.
//                        (0 is error and -1 is ignore implemented.
//        rgcatidImpl        list of implemented categories.
//                        should be NULL in the two cases mentioned above.
//
//        cRequired:        number of required categories.
//                        (0 is requiring nothing and -1 is ignore required.
//        rgcatidReq        list of required categories.
//                        should be NULL in the two cases mentioned above.
//
//        ppenumClsid        the enumerator of class ids.
//--------------------------------------------------------------------------

HRESULT __stdcall CClassContainer::EnumClassesOfCategories(ULONG cImplemented, CATID rgcatidImpl[],
                                                           ULONG cRequired, CATID rgcatidReq[],
                                                           IEnumGUID **ppenumClsid)
{
    ULONG                          i;
    CSCEnumClassesOfCategories    *penumclasses = NULL;
    HRESULT                        hr = S_OK;
    
    if (!IsValidPtrOut(ppenumClsid, sizeof(IEnumGUID *)))
        return E_INVALIDARG;
    
    if ((rgcatidImpl == NULL) && (cImplemented != 0) && (cImplemented != -1))
        return E_INVALIDARG;
    
    if ((rgcatidReq == NULL) && (cRequired != 0) && (cRequired != -1))
        return E_INVALIDARG;
    
    if ((cImplemented == -1) && (rgcatidImpl != NULL))
        return E_POINTER;
    
    if ((cRequired == -1) && (rgcatidReq != NULL))
        return E_POINTER;
    
    if (cImplemented == 0)
        return E_INVALIDARG;
    
    if ((rgcatidImpl) && (!IsValidReadPtrIn(rgcatidImpl, sizeof(CATID)*cImplemented)))
    {
        return E_INVALIDARG;
    }
    
    if ((rgcatidReq) && (!IsValidReadPtrIn(rgcatidReq, sizeof(CATID)*cRequired)))
    {
        return E_INVALIDARG;
    }
    
    if (!IsValidPtrOut(this, sizeof(*this)))
        return E_ACCESSDENIED;
    
    penumclasses = new CSCEnumClassesOfCategories;
    if (!penumclasses)
    {
        return E_OUTOFMEMORY;
    }
    
    // initialize the enumerator
    hr = penumclasses->Initialize(cRequired, rgcatidReq, cImplemented, rgcatidImpl,
        m_szClassName);
    ERROR_ON_FAILURE(hr);
    
    hr = penumclasses->QueryInterface(IID_IEnumCLSID, (void **)ppenumClsid);
    ERROR_ON_FAILURE(hr);

    return S_OK;

Error_Cleanup:
    if (penumclasses)
        delete penumclasses;
    
    return RemapErrorCode(hr, m_szContainerName);
} /* EnumClassesOfCategories */

//---------------------------------------------------------------
// EnumReqCategoriesOfClass:
//        see below EnumCategoriesofClass
//
//---------------------------------------------------------------

HRESULT CClassContainer::EnumReqCategoriesOfClass(REFCLSID rclsid, IEnumGUID **ppenumCatid)

{
    if (!IsValidReadPtrIn(this, sizeof(*this)))
        return E_ACCESSDENIED;
    
    if (IsNullGuid(rclsid))
        return E_INVALIDARG;
    
    if (!IsValidPtrOut(ppenumCatid, sizeof(IEnumGUID *)))
        return E_INVALIDARG;
    
    return EnumCategoriesOfClass(rclsid, REQ_CATEGORIES, ppenumCatid);
    
} /* EnumReqClassesOfCategories */

//---------------------------------------------------------------
// EnumImplCategoriesOfClass:
//        see below EnumCategoriesofClass
//
//---------------------------------------------------------------
HRESULT CClassContainer::EnumImplCategoriesOfClass(REFCLSID rclsid, IEnumGUID **ppenumCatid)
{
    if (!IsValidReadPtrIn(this, sizeof(*this)))
        return E_ACCESSDENIED;
    
    if (IsNullGuid(rclsid))
        return E_INVALIDARG;
    
    if (!IsValidPtrOut(ppenumCatid, sizeof(IEnumGUID *)))
        return E_INVALIDARG;
    
    return EnumCategoriesOfClass(rclsid, IMPL_CATEGORIES, ppenumCatid);
    
} /* EnumimplClassesOfCategories */

//---------------------------------------------------------------
// EnumCategoriesOfClass:
//        returns the enumerator for the implemented or required
//    rclsid:            the class id.
//    impl_or_req        the type of category to enumerated.
//    ppenumcatid        the enumerator that is returned.
// Prefetches all the catids and then enumerates them.
//---------------------------------------------------------------

HRESULT CClassContainer::EnumCategoriesOfClass(REFCLSID rclsid, BSTR impl_or_req,
                                               IEnumGUID **ppenumCatid)
{
    STRINGGUIDRDN                szRDN;
    HANDLE                       hADs = NULL;
    ULONG                        i;
    ULONG                        cCatid = 0, cgot = 0;
    CATID                      * Catid = NULL;
    CSCEnumCategoriesOfClass   * pEnumCatid = NULL;
    HRESULT                      hr = S_OK;
    WCHAR                      * szFullName = NULL;
    ADS_ATTR_INFO              * pAttr = NULL;
    
    if (!m_fOpen)
        return E_FAIL;
    
    // Get the ADs interface corresponding to the clsid that is mentioned.
    RDNFromGUID(rclsid, szRDN);
    
    BuildADsPathFromParent(m_szClassName, szRDN, &szFullName);
    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        &hADs);
    RETURN_ON_FAILURE(hr);
    
    // get the implemented or required cateogory list.
    hr = ADSIGetObjectAttributes(hADs, &impl_or_req, 1, &pAttr, &cgot);
    ERROR_ON_FAILURE(hr);
    
    if (cgot)
        hr = UnpackGUIDArrFrom(pAttr[0], &Catid, &cCatid);
    
    pEnumCatid = new CSCEnumCategoriesOfClass;
    
    if (!pEnumCatid)
    {
        hr = E_OUTOFMEMORY;
        ERROR_ON_FAILURE(hr);
    }
    
    // initialize the enumerator
    hr = pEnumCatid->Initialize(Catid, cCatid);
    ERROR_ON_FAILURE(hr);
    
    hr =  pEnumCatid->QueryInterface(IID_IEnumCATID, (void **)ppenumCatid);
    
Error_Cleanup:
    if (Catid)
        CoTaskMemFree(Catid);
    
    if (FAILED(hr)) {
        delete pEnumCatid;
        return hr;
    }
    
    if (szFullName)
        FreeADsMem(szFullName);
    
    if (pAttr)
        FreeADsMem(pAttr);
    
    if (hADs)
        ADSICloseDSObject(hADs);
    
    return RemapErrorCode(hr, m_szContainerName);

}
//---------------------------------------------------------------
// IsClassOfCategories:
//    similar to EnumClassesOfCategories but returns S_OK/S_FALSE for the
//    clsid rclsid. Finds the first class that implements these categories
//    and is of this clsid and checks its required.
//---------------------------------------------------------------

HRESULT __stdcall CClassContainer::IsClassOfCategories(REFCLSID rclsid, ULONG cImplemented,
                                                       CATID __RPC_FAR rgcatidImpl[  ],
                                                       ULONG cRequired, CATID __RPC_FAR rgcatidReq[ ])
{
    HRESULT             hr = S_OK;
    ADS_SEARCH_HANDLE   hADsSearchHandle = NULL;
    WCHAR               szfilter[_MAX_PATH];
    LPOLESTR            AttrNames[] = {IMPL_CATEGORIES, REQ_CATEGORIES, L"cn"};
    DWORD               cAttr = 3;
    STRINGGUID          szClsid;
    
    if (IsNullGuid(rclsid))
        return E_INVALIDARG;
    
    if (!IsValidPtrOut(this, sizeof(*this)))
        return E_ACCESSDENIED;
    
    if (cImplemented == 0)
        return E_INVALIDARG;
    
    if ((rgcatidImpl == NULL) && (cImplemented != 0) && (cImplemented != -1))
        return E_INVALIDARG;
    
    if ((rgcatidReq == NULL) && (cRequired != 0) && (cRequired != -1))
        return E_INVALIDARG;
    
    if ((cImplemented == -1) && (rgcatidImpl != NULL))
        return E_POINTER;
    
    if ((cRequired == -1) && (rgcatidReq != NULL))
        return E_POINTER;
    
    if ((rgcatidImpl) && (!IsValidReadPtrIn(rgcatidImpl, sizeof(CATID)*cImplemented)))
    {
        return E_INVALIDARG;
    }
    
    if ((rgcatidReq) && (!IsValidReadPtrIn(rgcatidReq, sizeof(CATID)*cRequired)))
    {
        return E_INVALIDARG;
    }
    
    StringFromGUID(rclsid, szClsid);
    
    wsprintf(szfilter, L"(cn=%s)", szClsid);
    
    // doing a search so that we can pass the same parameters to the
    // xxxSatisfied functions below.
    hr = ADSIExecuteSearch(m_ADsClassContainer, szfilter, AttrNames, cAttr, &hADsSearchHandle);
    RETURN_ON_FAILURE(hr);
    
    hr = ADSIGetFirstRow(m_ADsClassContainer, hADsSearchHandle);
    if ((FAILED(hr)) || (hr == S_ADS_NOMORE_ROWS))
    {
        hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
        ERROR_ON_FAILURE(hr);
    }
    
    hr = ImplSatisfied(cImplemented, rgcatidImpl, m_ADsClassContainer, hADsSearchHandle);
    ERROR_ON_FAILURE(hr);
    
    if (hr == S_OK)
    {
        hr = ReqSatisfied(cRequired, rgcatidReq, m_ADsClassContainer, hADsSearchHandle);
        ERROR_ON_FAILURE(hr);
    }
    
    if (hr != S_OK)
        hr =  S_FALSE;
    
Error_Cleanup:
    ADSICloseSearchHandle(m_ADsClassContainer, hADsSearchHandle);
    return hr;
    
} /* IsClassOfCategories */


//--------------------------------------------------------------------------------
//    ReqSatisfied:
//        Returns S_OK/S_FALSE depending on whether the clsid satisfies the required
//        condition for the clsid.
//    clsid:                Class ID of the class.
//    cAvailReq:            Number of Available required classes.
//    AvailReq:            Avail required classes.
//    calls the enumerator and sees whether there is any required class not present in
//    the available list. returns S_OK if cAvailReq = -1.
//--------------------------------------------------------------------------------

HRESULT ReqSatisfied(ULONG cAvailReq, CATID *AvailReq,
                     HANDLE hADs,
                     ADS_SEARCH_HANDLE hADsSearchHandle)
{
    HRESULT             hr = S_OK;
    ADS_SEARCH_COLUMN   column;
    GUID              * ReqGuid = NULL;
    DWORD               i, j, cReq = 0;
    
    if (cAvailReq == -1)
        return S_OK;
    
    hr = ADSIGetColumn(hADs, hADsSearchHandle, REQ_CATEGORIES, &column);
    if (FAILED(hr))
        return S_OK;
    
    hr = S_OK;
    
    UnpackGUIDArrFrom(column, &ReqGuid, &cReq);
    
    for (j = 0; j < cReq; j++) {
        /// check if the required categories are available
        for (i = 0; i < cAvailReq; i++)
            if (IsEqualGUID(ReqGuid[j], AvailReq[i]))
                break;
            if (i == cAvailReq) {
                hr = S_FALSE;
                break;
            }
    }
    
    CoTaskMemFree(ReqGuid);
    
    ADSIFreeColumn(hADs, &column);
    
    return hr;
}

//--------------------------------------------------------------------------------
//    Implements:
//        Returns S_OK/S_FALSE depending on whether the clsid satisfies the required
//        condition for the clsid.
//    clsid:                Class ID of the class.
//    cImplemented:         Number of Implemented categories.
//    ImplementedList:      Implemented Categories.
//    calls the enumerator and sees whether there is any required class not present in
//    the available list. returns S_OK if cImplemented = -1.
//--------------------------------------------------------------------------------

HRESULT ImplSatisfied(ULONG cImplemented, CATID *ImplementedList,
                      HANDLE hADs,
                      ADS_SEARCH_HANDLE hADsSearchHandle)
{
    ADS_SEARCH_COLUMN   column;
    GUID              * ImplGuid = NULL;
    ULONG               i, j, cImpl = 0;
    HRESULT             hr = S_FALSE;
    
    if (cImplemented == -1)
        return S_OK;
    
    hr = ADSIGetColumn(hADs, hADsSearchHandle, IMPL_CATEGORIES, &column);
    if (FAILED(hr))
        return S_FALSE;
    
    hr = S_FALSE;
    
    UnpackGUIDArrFrom(column, &ImplGuid, &cImpl);
    
    for (j = 0;j < cImpl; j++) {
        // check if it implements any of the categories requested.
        for (i = 0; i < cImplemented; i++)
            if (IsEqualGUID(ImplGuid[j], ImplementedList[i]))
                break;
            if (i < cImplemented) {
                hr = S_OK;
                break;
            }
    }
    
    CoTaskMemFree(ImplGuid);
    
    ADSIFreeColumn(hADs, &column);
    
    return hr;
}