//
//  Author: DebiM
//  Date:   January 97
//
//
//      Class Store Administration Implementation
//
//      This source file contains implementations for
//      IClassAdmin interface.
//
//      It uses ADs interfaces (over LDAP) to talk to an LDAP
//      provider such as NTDS.
//
//---------------------------------------------------------------------

#include "cstore.hxx"

// utility functions
HRESULT UpdateStoreUsn(HANDLE hADs, LPOLESTR szUsn)
{
    ADS_ATTR_INFO       pAttr[1];   
    DWORD               cModified = 0;
    HRESULT             hr = S_OK;
    
    PackStrToAttr(pAttr, STOREUSN, szUsn);
    hr = ADSISetObjectAttributes(hADs, pAttr, 1, &cModified);
    FreeAttr(pAttr[0]);
    return hr;
}



//----------------------------------------------------------
// Implementation for CClassContainer
//----------------------------------------------------------
//
CClassContainer::CClassContainer()

{
    m_fOpen = FALSE;
    m_ADsContainer = NULL;
    m_ADsClassContainer = NULL;
    m_ADsPackageContainer = NULL;
    m_ADsCategoryContainer = NULL;
    m_szCategoryName = NULL;
    m_szPackageName = NULL;
    m_szClassName = NULL;

    m_szPolicyName = NULL;
    memset (&m_PolicyId, 0, sizeof(GUID));
    
    m_uRefs = 1;
}


//---------------------------------------------------------------
//
//  Function:   Constructor
//
//  Synopsis:   Binds to the ClassStore given a class store path.
//
//  Arguments:
//  [in]    
//      szStoreName
//              Class Store Path without the leading ADCS:
//           
//  [out]
//      phr     
//              Sucess code.
//
//  Does an ADSI bind at the class store container and matches the
//  version numbers. if the version numbers match then it binds to the
//  class, package and category containers and keeps the bind handles.
//----------------------------------------------------------------

CClassContainer::CClassContainer(LPOLESTR szStoreName,
                                 HRESULT  *phr)
                                 
{
    DWORD               dwStoreVersion = 0;
    LPOLESTR            AttrNames[] = {STOREVERSION, POLICYDN, POLICYNAME};
    ADS_ATTR_INFO     * pAttrsGot = NULL;
    DWORD               cgot = 0, posn = 0;
    ADS_SEARCHPREF_INFO SearchPrefs[2];
    
    // set the search preference to one level search
    // and make the results come back in batches of size
    // 20 as default.
    SearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    SearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs[0].vValue.Integer = ADS_SCOPE_ONELEVEL;
    
    SearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
    SearchPrefs[1].vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs[1].vValue.Integer = 20;
    // BUGBUG: This should be defined as a constant.

    // initialising.
    *phr = S_OK;

    m_fOpen = FALSE;
    m_ADsContainer = NULL;
    m_ADsClassContainer = NULL;
    m_ADsPackageContainer = NULL;
    m_ADsCategoryContainer = NULL;
    m_szCategoryName = NULL;
    m_szPackageName = NULL;
    m_szClassName = NULL;    
    
    m_szPolicyName = NULL;
    memset (&m_PolicyId, 0, sizeof(GUID));

    // Bind to a Class Store Container Object
    // Cache the interface pointer
    //
    wcscpy (m_szContainerName, szStoreName);
    
    *phr = ADSIOpenDSObject(m_szContainerName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        &m_ADsContainer);
    
    ERROR_ON_FAILURE(*phr);
    
    //
    // Check the Schema Version of this container
    //
    
    *phr = ADSIGetObjectAttributes(m_ADsContainer, AttrNames, 3, &pAttrsGot, &cgot);
    
    if ((SUCCEEDED(*phr)) && (cgot))
    {
        posn = GetPropertyFromAttr(pAttrsGot, cgot, STOREVERSION);
        dwStoreVersion = 0;
        if (posn < cgot)
        {
            UnpackDWFrom(pAttrsGot[posn], &dwStoreVersion);
        }
        
        if ((!SUCCEEDED(*phr)) ||
            (dwStoreVersion != SCHEMA_VERSION_NUMBER))
        {
            *phr = CS_E_INVALID_VERSION;
        }

        if (SUCCEEDED(*phr))
        {
            LPOLESTR        szPolicyPath = NULL, szPolicyName = NULL;

            posn = GetPropertyFromAttr(pAttrsGot, cgot, POLICYDN);
            if (posn < cgot)
            {
                LPOLESTR        szParentPath = NULL, szPolicyGuid = NULL;
                
                UnpackStrFrom(pAttrsGot[posn], &szPolicyPath); 
                //

                BuildADsParentPath(szPolicyPath, &szParentPath, &szPolicyGuid);
                if (szParentPath)
                    FreeADsMem(szParentPath);

                if (szPolicyGuid)
                {
                    if (wcslen(szPolicyGuid) == 41)
                    {
                        // valid GUID

                        GUIDFromString(&szPolicyGuid[4], &m_PolicyId);
                    }
                    FreeADsMem(szPolicyGuid);
                }
            }

            posn = GetPropertyFromAttr(pAttrsGot, cgot, POLICYNAME);
            if (posn < cgot)
            {
                UnpackStrAllocFrom(pAttrsGot[posn], &m_szPolicyName); 
            }
        }
    }
    else
    {
        *phr = CS_E_INVALID_VERSION;
    }
    
    if (pAttrsGot)
        FreeADsMem(pAttrsGot);

    ERROR_ON_FAILURE(*phr);
    
    //
    // Bind to the class container Object
    // Cache the interface pointer
    //
    
    // get the class container name.
    BuildADsPathFromParent(m_szContainerName, CLASSCONTAINERNAME, &m_szClassName);
    
    m_ADsClassContainer = NULL;
    
    // bind to the class container.
    *phr = ADSIOpenDSObject(m_szClassName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        &m_ADsClassContainer);
    
    ERROR_ON_FAILURE(*phr);
    
    // set the search preference on the handle.
    *phr = ADSISetSearchPreference(m_ADsClassContainer, SearchPrefs, 2);
    
    ERROR_ON_FAILURE(*phr);
    
    //
    // Bind to the Package container Object
    // Cache the interface pointer
    //
    
    // get the package container name.
    BuildADsPathFromParent(m_szContainerName, PACKAGECONTAINERNAME, &m_szPackageName);
    
    m_ADsPackageContainer = NULL;
    
    // bind to the package container.
    *phr = ADSIOpenDSObject(m_szPackageName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
                            &m_ADsPackageContainer);
    
    ERROR_ON_FAILURE(*phr);
    
    // set the search preference.
    *phr = ADSISetSearchPreference(m_ADsPackageContainer, SearchPrefs, 2);
    ERROR_ON_FAILURE(*phr);
    
    //
    // Bind to the category container Object
    // Cache the interface pointer
    //

    // get the category container name
    BuildADsPathFromParent(m_szContainerName, CATEGORYCONTAINERNAME, &m_szCategoryName);
    
    m_ADsCategoryContainer = NULL;
    
    // bind to the category container.
    *phr = ADSIOpenDSObject(m_szCategoryName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
                            &m_ADsCategoryContainer);    
    ERROR_ON_FAILURE(*phr);
    
    // set the search preferences.
    *phr = ADSISetSearchPreference(m_ADsCategoryContainer, SearchPrefs, 2);
    ERROR_ON_FAILURE(*phr);
    
    m_fOpen = TRUE;
    m_uRefs = 1;
    
Error_Cleanup:
    *phr = RemapErrorCode(*phr, m_szContainerName);
    return;
}


CClassContainer::~CClassContainer(void)
{
    if (m_fOpen)
    {
        m_fOpen = FALSE;
    }
    
    if (m_ADsClassContainer)
    {
        ADSICloseDSObject(m_ADsClassContainer);
        m_ADsClassContainer = NULL;
    }
    // the bind might have failed while we succeeded in getting a path.
    if (m_szClassName)
        FreeADsMem(m_szClassName);

    if (m_ADsPackageContainer)
    {
        ADSICloseDSObject(m_ADsPackageContainer);
        m_ADsPackageContainer = NULL;
    }

    if (m_szPackageName)
        FreeADsMem(m_szPackageName);
    
    if (m_ADsCategoryContainer)
    {
        ADSICloseDSObject(m_ADsCategoryContainer);
        m_ADsCategoryContainer = NULL;
    }
    
    if (m_szCategoryName)
        FreeADsMem(m_szCategoryName);

    if (m_ADsContainer)
    {
        ADSICloseDSObject(m_ADsContainer);
        m_ADsContainer = NULL;
    }
}

// currently unused.
BOOL InvalidDSName(LPWSTR pName)
{

    if (wcslen(pName) >= 56)
        return TRUE;

    while (*pName)
    {
        if ((*pName == L':') ||
            (*pName == L',') ||
            (*pName == L';') ||
            (*pName == L'/') ||
            (*pName == L'<') ||
            (*pName == L'>') ||
            (*pName == L'\\')||
            (*pName == L'+'))
            return TRUE;
        ++pName;
    }

    return FALSE;
}

HRESULT  CClassContainer::GetGPOInfo(GUID *pGPOId, LPOLESTR *pszPolicyName)
{
    if ((!pGPOId) || (!IsValidPtrOut(pGPOId, sizeof(GUID))))
        return E_INVALIDARG;

    if ((!pszPolicyName) || (!IsValidPtrOut(pszPolicyName, sizeof(LPOLESTR))))
        return E_OUTOFMEMORY;

    memcpy(pGPOId, &m_PolicyId, sizeof(GUID));

    if (m_szPolicyName)
    {
        *pszPolicyName = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*(1+wcslen(m_szPolicyName)));
        if (!(*pszPolicyName))
            return E_INVALIDARG;
        wcscpy(*pszPolicyName, m_szPolicyName);
    }
    else
    {
        *pszPolicyName = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*2);
        if (!(*pszPolicyName))
            return E_OUTOFMEMORY;

        (*pszPolicyName)[0] = L'\0';
    }
    return S_OK;
}

//
// Removing a class from the database
//

//---------------------------------------------------------------
//
//  Function:   DeleteClass
//
//  Synopsis:   Internal function. Deletes the clsid from the ClassContainer.
//
//  UsedBy      RemovePackage
//
//
//  Arguments:
//  [in]    
//      szClsid
//              Stringised Clsid
//   
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//
//  Binds to the clsid object under the class container.
//  and decrements the reference count. If the reference
//  count goes to zero, then it deletes the clsid.
//  BUGBUG: If 2 objects try to delete it at the same
//  time this might not work properly.
//----------------------------------------------------------------

HRESULT CClassContainer::DeleteClass (LPOLESTR szClsid)
{
    WCHAR           szName[_MAX_PATH], *szFullName = NULL;
    HRESULT         hr = S_OK;
    DWORD           refcount = 0, cgot = 0, cAttr = 1, cModified = 0;
    HANDLE          hADs = NULL;
    LPOLESTR        AttrName = CLASSREFCOUNTER;
    ADS_ATTR_INFO * pAttr = NULL;
    ADS_ATTR_INFO   Attr;
    
    if (!m_fOpen)
        return E_FAIL;
        
    // constructs the fullname from the clsid and class container Name.
    wsprintf(szName, L"CN=%s", szClsid);
    BuildADsPathFromParent(m_szClassName, szName, &szFullName);
    
    // binds to the class object.
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        &hADs);
    
    if (szFullName)
        FreeADsMem(szFullName);

    ERROR_ON_FAILURE(hr);

    // gets the reference count attribute.
    hr = ADSIGetObjectAttributes(hADs, &AttrName, 1, &pAttr, &cgot);
    
    if ((SUCCEEDED(hr)) && (cgot))
        UnpackDWFrom(pAttr[0], (DWORD *)&refcount);
    
    if (pAttr)
        FreeADsMem(pAttr);
    
    if (refcount <= 1) {
        // Delete the object if the reference count is less than zero.
        hr = ADSIDeleteDSObject(m_ADsClassContainer, szName);
        if (hADs)
            ADSICloseDSObject(hADs);
    }
    else {
        // Decrement the reference count and store it back.
        refcount--;
        PackDWToAttr(&Attr, CLASSREFCOUNTER, refcount);
        hr = ADSISetObjectAttributes(hADs, &Attr, cAttr, &cModified);
        ADSICloseDSObject(hADs);
        FreeAttr(Attr);
    }
        
Error_Cleanup:
    return RemapErrorCode(hr, m_szContainerName);
}

//---------------------------------------------------------------
//
//  Function:   EnumPackages
//
//  Synopsis:   Returns an Enumerator for all the packages that satisfies
//              the query.
//
//  UsedBy      Add/Remove Programs
//
//  Arguments:
//  [in]    
//      pszFileExt
//              FileExt that has to be queried on. ignored if NULL.
//      pCategory
//              Category that has to be queried on. ignored if NULL.
//      dwAppFlags
//              One of the APPINFO_XXX. ignored if it is APPINFO_ALL.
//      pdwLocale
//              Locale that has to be queried on. Ignored if NULL.
//      pPlatform
//              Platform that has to be queried on. Ignored if NULL.
//              
//  [out]
//      ppIEnumPackage
//              Enumerator is returned. 
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//
//  Validates the inputs, Creates a EnumPackage object, makes up the
//  search string made up of file extension, category.
//----------------------------------------------------------------
HRESULT CClassContainer::EnumPackages(
                                      LPOLESTR           pszFileExt,
                                      GUID              *pCategory,
                                      DWORD              dwAppFlags,
                                      DWORD             *pdwLocale,
                                      CSPLATFORM        *pPlatform,
                                      IEnumPackage     **ppIEnumPackage
                                      )
{
    HRESULT                     hr = S_OK;
    CEnumPackage               *pEnum = NULL;
    WCHAR                       szfilter[1000], szQry[1000];

    if (pszFileExt && IsBadStringPtr(pszFileExt, _MAX_PATH))
        return E_INVALIDARG;
    
    if (pCategory && !IsValidReadPtrIn(pCategory, sizeof(GUID)))
        return E_INVALIDARG;
    
    if (!IsValidPtrOut(ppIEnumPackage, sizeof(IEnumPackage *)))
        return E_INVALIDARG;
    
    *ppIEnumPackage = NULL;
    
    pEnum = new CEnumPackage(m_PolicyId, m_szPolicyName);
    if(NULL == pEnum)
        return E_OUTOFMEMORY;
    
    //
    // Create a CommandText
    //
    wsprintf(szfilter, L"(& (objectClass=%s) ", CLASS_CS_PACKAGE);
        
    if (pszFileExt)
    {
        wsprintf(szQry, L"(%s=%s*) ", PKGFILEEXTNLIST, pszFileExt);
        wcscat(szfilter, szQry);
    }
    
    if (pCategory)
    {
        STRINGGUID szCat;
        StringFromGUID (*pCategory, szCat);
        wsprintf(szQry, L"(%s=%s) ", PKGCATEGORYLIST, szCat);
        wcscat(szfilter, szQry);
    }
    
    wcscat(szfilter, L")");
    
    hr = pEnum->Initialize(m_szPackageName, szfilter,
        dwAppFlags, pPlatform);
    
    ERROR_ON_FAILURE(hr);
    
    hr = pEnum->QueryInterface(IID_IEnumPackage, (void**)ppIEnumPackage);
    ERROR_ON_FAILURE(hr);
    
    return S_OK;
    
Error_Cleanup:
    if (pEnum)
        delete pEnum;
    *ppIEnumPackage = NULL;
    
    return RemapErrorCode(hr, m_szContainerName);
}

HRESULT CClassContainer::GetDNFromPackageName(LPOLESTR pszPackageName, LPOLESTR *szDN)
{
    HRESULT             hr = S_OK;
    WCHAR               szfilter[_MAX_PATH];
    LPOLESTR            AttrNames[] = {OBJECTCLASS, PACKAGEFLAGS, OBJECTDN};
    DWORD               cAttr = 3;
    ADS_SEARCH_HANDLE   hADsSearchHandle = NULL;
    ADS_SEARCH_COLUMN   column;
    DWORD               dwFlags = 0;

    wsprintf(szfilter, L"%s=%s", PACKAGENAME, pszPackageName);

    hr = ADSIExecuteSearch(m_ADsPackageContainer, szfilter, AttrNames, cAttr, &hADsSearchHandle);
    RETURN_ON_FAILURE(hr);    

    for (hr = ADSIGetFirstRow(m_ADsPackageContainer, hADsSearchHandle);
	            ((SUCCEEDED(hr)) && (hr != S_ADS_NOMORE_ROWS));
	            hr = ADSIGetNextRow(m_ADsPackageContainer, hADsSearchHandle))
    {
        hr = ADSIGetColumn(m_ADsPackageContainer, hADsSearchHandle, PACKAGEFLAGS, &column);
        if (SUCCEEDED(hr))
        {
            UnpackDWFrom(column, &dwFlags);
            
            ADSIFreeColumn(m_ADsPackageContainer, &column);
        }
        else
            continue;

        if ((dwFlags & ACTFLG_Orphan) || (dwFlags & ACTFLG_Uninstall))
            continue;

        hr = ADSIGetColumn(m_ADsPackageContainer, hADsSearchHandle, OBJECTDN, &column);
        if (SUCCEEDED(hr))
        {
            UnpackStrAllocFrom(column, szDN);
            
            ADSIFreeColumn(m_ADsPackageContainer, &column);
        }
        else
            continue;

        break;

    }
    
    if (hADsSearchHandle)
        ADSICloseSearchHandle(m_ADsPackageContainer, hADsSearchHandle);
    
    return hr;
}

// Gets the RDN of a package given the package Guid. 
HRESULT CClassContainer::BuildDNFromPackageGuid(GUID PackageGuid, LPOLESTR *szDN)
{
    HRESULT             hr = S_OK;
    LPOLESTR            AttrName = {OBJECTDN};
    WCHAR               szfilter[_MAX_PATH];
    ADS_SEARCH_HANDLE   hADsSearchHandle = NULL;
    ADS_SEARCH_COLUMN   column;
    LPWSTR              EncodedGuid = NULL;

    hr = ADsEncodeBinaryData((PBYTE)&PackageGuid, sizeof(GUID), &EncodedGuid);

    wsprintf(szfilter, L"(%s=%s)", OBJECTGUID, EncodedGuid);

    FreeADsMem(EncodedGuid);

    hr = ADSIExecuteSearch(m_ADsPackageContainer, szfilter, &AttrName, 1, &hADsSearchHandle);

    RETURN_ON_FAILURE(hr);

    hr = ADSIGetFirstRow(m_ADsPackageContainer, hADsSearchHandle);
    if ((FAILED(hr)) || (hr == S_ADS_NOMORE_ROWS))
    {
        ERROR_ON_FAILURE(hr = CS_E_PACKAGE_NOTFOUND);
    }

    hr = ADSIGetColumn(m_ADsPackageContainer, hADsSearchHandle, AttrName, &column);
    ERROR_ON_FAILURE(hr);

    if (SUCCEEDED(hr))
    {
        UnpackStrAllocFrom(column, szDN);
    }

    ADSIFreeColumn(m_ADsPackageContainer, &column);

Error_Cleanup:
    if (hADsSearchHandle)
        ADSICloseSearchHandle(m_ADsPackageContainer, hADsSearchHandle);

    return hr;
}

HRESULT CClassContainer::GetPackageGuid(WCHAR *szRDN, GUID *pPackageGuid)
{
    HRESULT             hr = S_OK;
    LPOLESTR            AttrName = {OBJECTGUID};
    WCHAR               szfilter[_MAX_PATH];
    ADS_SEARCH_HANDLE   hADsSearchHandle = NULL;
    ADS_SEARCH_COLUMN   column;

    hr = ADSIExecuteSearch(m_ADsPackageContainer, szRDN, &AttrName, 1, &hADsSearchHandle);

    RETURN_ON_FAILURE(hr);

    hr = ADSIGetFirstRow(m_ADsPackageContainer, hADsSearchHandle);
    if ((FAILED(hr)) || (hr == S_ADS_NOMORE_ROWS))
    {
        ERROR_ON_FAILURE(hr = CS_E_PACKAGE_NOTFOUND);
    }

    hr = ADSIGetColumn(m_ADsPackageContainer, hADsSearchHandle, AttrName, &column);
    ERROR_ON_FAILURE(hr);

    if (SUCCEEDED(hr))
        UnpackGUIDFrom(column, pPackageGuid);

    ADSIFreeColumn(m_ADsPackageContainer, &column);

Error_Cleanup:
    if (hADsSearchHandle)
        ADSICloseSearchHandle(m_ADsPackageContainer, hADsSearchHandle);

    return hr;
}
//---------------------------------------------------------------
//
//  Function:   GetPackageDetails
//
//  Synopsis:   Returns the PackageDetail corresponding to the PackageName.
//
//  Arguments:
//  [in]    
//      pszPackageId
//              Id of the Package.
//  [out]
//      pPackageDetail
//              PackageDetail info that is returned.
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//
//  Binds to the Package object and calls GetPackageDetail with it.
//----------------------------------------------------------------
HRESULT CClassContainer::GetPackageDetails (
                                            LPOLESTR          pszPackageName,
                                            PACKAGEDETAIL   * pPackageDetail
                                            )
{
    HRESULT              hr = S_OK;
    HANDLE               hADs = NULL;
    WCHAR              * szFullName = NULL;
    ADS_ATTR_INFO      * pAttr = NULL;
    DWORD                cgot = 0;
    
    // this can be made into a search based API. Not required for the time being.
    // Should change it if perf is a big issue.
    if ((!pszPackageName) || IsBadStringPtr(pszPackageName, _MAX_PATH))
        return E_INVALIDARG;

    hr = GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;
    
    // binding to the package object.    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION, &hADs);
    ERROR_ON_FAILURE(hr);
    
    // calling GetPackageDetail.
    hr = GetPackageDetail (hADs, m_szClassName, pPackageDetail);
    
    ADSICloseDSObject(hADs);
    
    if (pAttr)
        FreeADsMem(pAttr);
    
    if (szFullName)
        CoTaskMemFree(szFullName);
    
Error_Cleanup:
    return RemapErrorCode(hr, m_szContainerName);
}


//---------------------------------------------------------------
//
//  Function:   GetPackageDetails
//
//  Synopsis:   Returns the PackageDetail corresponding to the PackageName.
//
//  Arguments:
//  [in]    
//      pszPackageId
//              Id of the Package.
//  [out]
//      pPackageDetail
//              PackageDetail info that is returned.
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//
//  Binds to the Package object and calls GetPackageDetail with it.
//----------------------------------------------------------------
HRESULT CClassContainer::GetPackageDetailsFromGuid (
                                                    GUID            PkgGuid,
                                                    PACKAGEDETAIL  *pPackageDetail
                                                   )
{
    HRESULT              hr = S_OK;
    HANDLE               hADs = NULL;
    WCHAR              * szFullName = NULL, szRDN[_MAX_PATH];
    ADS_ATTR_INFO      * pAttr = NULL;
    DWORD                cgot = 0;
    
    // this can be made into a search based API. Not required for the time being.
    // Should change it if perf is a big issue.
    if (IsNullGuid(PkgGuid))
        return E_INVALIDARG;

    BuildDNFromPackageGuid(PkgGuid, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;
    
    // binding to the package object.
    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION, &hADs);
    ERROR_ON_FAILURE(hr);
    
    // calling GetPackageDetail.
    hr = GetPackageDetail (hADs, m_szClassName, pPackageDetail);
    
    ADSICloseDSObject(hADs);
    
    if (pAttr)
        FreeADsMem(pAttr);
    
    if (szFullName)
        CoTaskMemFree(szFullName);

Error_Cleanup:
    return RemapErrorCode(hr, m_szContainerName);
}

#define FREEARR(ARR, SZ) {                                          \
                DWORD curIndex;                                     \
                for (curIndex = 0; curIndex < (SZ); curIndex++)     \
                    CoTaskMemFree((ARR)[curIndex]);                 \
                CoTaskMemFree(ARR);                                 \
                ARR = NULL;                                         \
        }                                                           \


//---------------------------------------------------------------
//
//  Function:   ChangePackageUpgradeInfoIncremental
//
//  Synopsis:   Mark the package as upgraded by another package
//
//  Arguments:
//  [in]    
//      PackageGuid
//              Package Guid to identify the package.
//      szUpgradedByClassStore
//              Class Store where the package that upgrades exists
//      UpgradedByPackageGuid
//              The Guid of the package that upgrades
//      Add     Add or remove the upgradedByrelation
//
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//  Otherwise it packs all the required attributes in the ATTR_INFO
//  structure and sends it to the Directory.
//----------------------------------------------------------------
HRESULT CClassContainer::ChangePackageUpgradeInfoIncremental(
                         GUID           PackageGuid,
                         UPGRADEINFO    UpgradeInfo,
                         DWORD          OpFlags
                      )
{
    HRESULT         hr = S_OK;
    HANDLE          hADs = NULL;
    WCHAR          *szFullName=NULL, szGuid[_MAX_PATH], szUsn[20];
    LPOLESTR        pProp = NULL;
    ADS_ATTR_INFO   pAttr[2];
    DWORD           cAttr = 0, cModified = 0, i=0;
    UINT            len=0;

    hr = BuildDNFromPackageGuid(PackageGuid, &szFullName);
    ERROR_ON_FAILURE(hr);
    
    // Bind to the Package Object.
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, &hADs);
    ERROR_ON_FAILURE(hr);

    StringFromGUID(UpgradeInfo.PackageGuid, szGuid);

    len = wcslen(UpgradeInfo.szClassStore);
    pProp = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) *(36+PKG_UPG_DELIM1_LEN+len+PKG_UPG_DELIM2_LEN+2+2+1));
                    // Guid size+::+length++:+flagDigit+2 

    wsprintf(pProp, L"%s%s%s%s%02x", UpgradeInfo.szClassStore, PKG_UPG_DELIMITER1, szGuid, PKG_UPG_DELIMITER2, UpgradeInfo.Flag%16);

    PackStrArrToAttrEx(pAttr+cAttr, UPGRADESPACKAGES, &pProp, 1, OpFlags?TRUE:FALSE);
    cAttr++;

    //
    // Update the TimeStamp
    //
    GetCurrentUsn(szUsn);
    
    PackStrToAttr(pAttr+cAttr, PKGUSN, szUsn);
    cAttr++;

    hr = ADSISetObjectAttributes(hADs, pAttr, cAttr,  &cModified);

    if (hADs)
        ADSICloseDSObject(hADs);
    
    // ignore it if the property already exists.
    if (hr == HRESULT_FROM_WIN32(ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS))
        hr = S_OK;

    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
    
    
Error_Cleanup:
    if (szFullName)
        CoTaskMemFree(szFullName);

    return RemapErrorCode(hr, m_szContainerName);
}


//---------------------------------------------------------------
//
//  Function:   ChangePackageProperties
//
//  Synopsis:   Change Various (most commonly changed) properties 
//              for a given package.
//
//  Arguments:
//  [in]    
//      PackageId
//              Package Id to identify the package.
//      pszNewname
//              new Name for the Package. If it is being renamed
//              all other changes should be NULL.
//      pdwFlags
//              The Deployment Flags. Should be ACTFLG_XXX
//              Ignored if NULL.
//      pszUrl
//              Help Url for the Package. Ignored if NULL.
//      pszScriptPath
//              Script Path for the Package. Ignored if NULL.
//      pInstallUiLevel
//              The InstallationUiLevel. Ignored if NULL.
//      pdwRevision
//              REVISION. Ignored if NULL.
//              
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//  Otherwise it packs all the required attributes in the ATTR_INFO
//  structure and sends it to the Directory.
//----------------------------------------------------------------
HRESULT CClassContainer::ChangePackageProperties(
                                                 LPOLESTR       pszPackageName,
                                                 LPOLESTR       pszNewName,
                                                 DWORD         *pdwFlags,
                                                 LPOLESTR       pszUrl,
                                                 LPOLESTR       pszScriptPath,
                                                 UINT          *pInstallUiLevel,
                                                 DWORD         *pdwRevision
                                                 )
{
    HRESULT     hr = S_OK;
    HANDLE      hADs = NULL;
    WCHAR      *szRDN=NULL, *szFullName=NULL;
    WCHAR       szUsn[20];
    ADS_ATTR_INFO pAttr[7];
    DWORD       cAttr = 0, cModified = 0, i=0;
    
    if ((!pszPackageName) || IsBadStringPtr(pszPackageName, _MAX_PATH))
        return E_INVALIDARG;

    hr = GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;

    // if no properties have to be changed.
    if (!(pszScriptPath || pszUrl || pdwFlags || pInstallUiLevel || pdwRevision || pszNewName))
        return E_INVALIDARG;

    if (pszNewName)
    {
        // rename package

        WCHAR    szNewRDN[_MAX_PATH];
        BOOL     GenerateGuid = FALSE;
        GUID     PackageGuidId;
        WCHAR    pszPackageNewId[_MAX_PATH], *szJunk = NULL;   

        if (IsBadStringPtr(pszNewName, _MAX_PATH))
            return E_INVALIDARG;
        
        if (pszScriptPath || pszUrl || pdwFlags || pInstallUiLevel || pdwRevision)
            return E_INVALIDARG;

        // see whether the new name is valid.
//        GenerateGuid = InvalidDSName(pszNewName);

        // see whether the newName already exists. Notice that if the same package name is 
        // entered it will return error.
        hr = GetDNFromPackageName(pszNewName, &szJunk);

        if (szJunk)
            CoTaskMemFree(szJunk);
        szJunk = NULL;

        ERROR_ON_FAILURE(hr);

        if (hr == S_OK)
            return CS_E_OBJECT_ALREADY_EXISTS;
/*
        packages are going to have a guid as a name and nothing else.        
        // generate guid if required.
        if (GenerateGuid)
        {
            CoCreateGuid(&PackageGuidId);
            StringFromGUID(PackageGuidId, pszPackageNewId);
        }
        else 
            wcscpy(pszPackageNewId, pszNewName);
        
        // generate the new RDN
        wsprintf(szNewRDN, L"CN=%s", pszPackageNewId);

        BuildADsParentPath(szFullName, &szJunk, &szRDN);
        
        if (szJunk)
            FreeADsMem(szJunk);

        hr = ADSIModifyRdn(m_ADsPackageContainer, szRDN, szNewRDN);  

        if (szRDN)
            FreeADsMem(szRDN);

        ERROR_ON_FAILURE(hr);

        if (szFullName)
            CoTaskMemFree(szFullName);
        szFullName = NULL;

        // construct the Full Path for the Package.
        BuildADsPathFromParent(m_szPackageName, szNewRDN, &szFullName);
*/
        // Bind to the Package Object.
        hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, 
                          &hADs);
        if (szFullName)
            FreeADsMem(szFullName);
        szFullName = NULL;

        ERROR_ON_FAILURE(hr);
    }
    else 
    {
        // Bind to the Package Object.
        hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, 
            &hADs);
        ERROR_ON_FAILURE(hr);

        if (szFullName)
            CoTaskMemFree(szFullName);
        szFullName = NULL;
    }
    
    // Just change some properties.
    //
    // Update the TimeStamp
    //
    
    GetCurrentUsn(szUsn);
    
    PackStrToAttr(pAttr+cAttr, PKGUSN, szUsn);
    cAttr++;
    
    //
    // Change Package Flags
    //
    if (pdwFlags)
    {
        PackDWToAttr (pAttr+cAttr, PACKAGEFLAGS, *pdwFlags);
        cAttr++;
    }
    
    //
    // Change Package Script
    //
    if (pszScriptPath) 
    {
        PackStrToAttr(pAttr+cAttr, SCRIPTPATH, pszScriptPath);
        cAttr++;
    }
    
    //
    // Change Package Help URL
    //
    if (pszUrl) 
    {
        PackStrToAttr(pAttr+cAttr, HELPURL, pszUrl);
        cAttr++;
    }
    
    //
    // Change Package UI Level.
    //
    if (pInstallUiLevel) 
    {
        PackDWToAttr (pAttr+cAttr, UILEVEL, *pInstallUiLevel);
        cAttr++;
    }
    
    //
    // Change Revision.
    //
    if (pdwRevision) 
    {
        PackDWToAttr (pAttr+cAttr, REVISION, *pdwRevision);
        cAttr++;
    }
    
    if (pszNewName)
    {
        PackStrToAttr(pAttr+cAttr, PACKAGENAME, pszNewName);
        cAttr++;
    }

    hr = ADSISetObjectAttributes(hADs, pAttr, cAttr,  &cModified);

    if (hADs)
        ADSICloseDSObject(hADs);
    
    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
    
    if (SUCCEEDED(hr))
    {
        //
        // Update Class Store Usn
        //
        UpdateStoreUsn(m_ADsContainer, szUsn);
    }
    
Error_Cleanup:
    if (szFullName)
        CoTaskMemFree(szFullName);
    return RemapErrorCode(hr, m_szContainerName);
}
//---------------------------------------------------------------
//  Function:   ChangePackageCategories
//
//  Synopsis:   Change (Not Add) the Categories that a package
//              belongs to.
//
//  Arguments:
//  [in]    
//      pszPackageName
//              Package Name to identify the package.
//      cCategories
//              Number of Categories.
//      rpCategory
//              Array of categories.
//              
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//  Binds to the Package Object, Converts all the categories to strings
//  Packs it and sends it to the DS.
//----------------------------------------------------------------
HRESULT CClassContainer::ChangePackageCategories(
                                                 LPOLESTR       pszPackageName,
                                                 UINT           cCategories,
                                                 GUID          *rpCategory
                                                 )
{
    //
    // Does not change USN
    //
    HRESULT     hr = S_OK;
    HANDLE      hADs = NULL;
    WCHAR      *szFullName = NULL;
    LPOLESTR   *pszGuid = NULL;
    UINT        count;
    ADS_ATTR_INFO pAttr[1];
    DWORD       cAttr = 0, cModified = 0, i=0;
    
    if ((!pszPackageName) || IsBadStringPtr(pszPackageName, _MAX_PATH))
        return E_INVALIDARG;

    if ((cCategories) && ((!rpCategory) ||
           (!IsValidReadPtrIn(rpCategory, sizeof(GUID) * cCategories))))
        return E_INVALIDARG;

    // Construct the Name of the Package Object.

    GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;
       
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
                          &hADs);
    ERROR_ON_FAILURE(hr);
    
    // fill in the categories
    pszGuid = (LPOLESTR *)CoTaskMemAlloc(cCategories * sizeof(LPOLESTR));
    if (!pszGuid) 
    {
        hr = E_OUTOFMEMORY;
        ERROR_ON_FAILURE(hr);
    }
    
    // convert the GUIDs to Strings.
    for (count = 0; (count < cCategories); count++) 
    {
        pszGuid[count] = (LPOLESTR)CoTaskMemAlloc(STRINGGUIDLEN*sizeof(WCHAR));
        
        if (!(pszGuid[count])) 
        {
            FREEARR(pszGuid, count);
            ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
        }
        
        StringFromGUID(rpCategory[count], pszGuid[count]);
    }
    
    // Pack it into Attribute Structure.
    PackStrArrToAttr(pAttr+cAttr, PKGCATEGORYLIST, pszGuid, cCategories);
    cAttr++;
    
    // Set the Attribute
    hr = ADSISetObjectAttributes(hADs, pAttr, cAttr,  &cModified);
    
Error_Cleanup:
    if (hADs)
        ADSICloseDSObject(hADs);
    
    if (pszGuid)
        for (count = 0; (count < cCategories); count++)
            CoTaskMemFree(pszGuid[count]);
    
    CoTaskMemFree(pszGuid);

    if (szFullName)
        CoTaskMemFree(szFullName);

    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
    
    return RemapErrorCode(hr, m_szContainerName);
}


//---------------------------------------------------------------
//  Function:   SetPriorityByFileExt
//
//  Synopsis:   Changes the priority of a Package corresp. to
//              a file Extension.
//
//  Arguments:
//  [in]    
//      pszPackageName
//              Package Name to identify the package.
//      pszFileExt
//              File Extension for which the priority has to be changed.
//      Priority
//              Priority for the Package.
//              
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//  Binds to the Package Object, Gets the file Extensions and changes
//  the priority corresponding to the File Extension.
//----------------------------------------------------------------
HRESULT CClassContainer::SetPriorityByFileExt(
                                              LPOLESTR pszPackageName,
                                              LPOLESTR pszFileExt,
                                              UINT     Priority
                                              )
{
    HRESULT       hr = S_OK;
    HANDLE        hADs = NULL;
    WCHAR        *szFullName=NULL;
    LPOLESTR     *prgFileExt = NULL;
    WCHAR         szUsn[20];
    ADS_ATTR_INFO pAttr[4], *pAttrGot = NULL;
    DWORD         cAttr = 0, cAttrGot = 0, cModified = 0, cShellFileExt = 0, i=0;
    LPOLESTR      pAttrNames[] = {PKGFILEEXTNLIST};
    
    // Construct the Package Name
    GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;
        
    // Bind to the Package Object.
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, 
                          &hADs);
    ERROR_ON_FAILURE(hr);
    
    //
    // Update the TimeStamp
    //
    GetCurrentUsn(szUsn);
    
    PackStrToAttr(pAttr+cAttr, PKGUSN, szUsn);
    cAttr++;
    
    // get the file extensions.
    hr = ADSIGetObjectAttributes(hADs, pAttrNames, 1,  &pAttrGot,  &cAttrGot);    
    
    if  ((SUCCEEDED(hr)) && (cAttrGot))  
        UnpackStrArrFrom(pAttrGot[0], &prgFileExt, &cShellFileExt);
    
    // Look for the given file extension.
    for (i=0; i < cShellFileExt; ++i)
    {
        if (wcsncmp(prgFileExt[i], pszFileExt, wcslen(pszFileExt)) == 0)
        {
            // if the file extension is found, change the corresponding priority.
            if (wcslen(prgFileExt[i]) != (wcslen(pszFileExt)+3))
                continue;

            wsprintf(prgFileExt[i], L"%s:%2d", pszFileExt, Priority%100);
            break;
        }
    }
    
    if (i == cShellFileExt)
    {
        ERROR_ON_FAILURE(hr = CS_E_OBJECT_NOTFOUND);
    }
    
    if (cShellFileExt)
    {
        PackStrArrToAttr(pAttr+cAttr, PKGFILEEXTNLIST, prgFileExt, cShellFileExt);
        cAttr++;
    }
    
    hr = ADSISetObjectAttributes(hADs,  pAttr, cAttr,  &cModified);
    if (SUCCEEDED(hr))
    {
        //
        // Update Store Usn
        //
        UpdateStoreUsn(m_ADsContainer, szUsn);
    }
    
Error_Cleanup:    
    CoTaskMemFree(prgFileExt);
    
    if (szFullName)
        CoTaskMemFree(szFullName);

    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
    
    if (pAttrGot)
        FreeADsMem(pAttrGot);
    
    ADSICloseDSObject(hADs);
    
    return RemapErrorCode(hr, m_szContainerName);
}

//---------------------------------------------------------------
//  Function:   ChangePackageSourceList
//
//  Synopsis:   Changes the priority of a Package corresp. to
//              a file Extension.
//
//  Arguments:
//  [in]    
//      pszPackageName
//              Package Name to identify the package.
//      cSources
//              Number of sources
//      pszSourceList
//              List of sources
//              
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//  Binds to the Package Object, Makes the new sourcelist with the order
// maintained.
//----------------------------------------------------------------
HRESULT CClassContainer::ChangePackageSourceList(
                                                LPOLESTR     pszPackageName,
                                                UINT         cSources,
                                                LPOLESTR    *pszSourceList
                                                )
{
    HRESULT     hr = S_OK;
    HANDLE      hADs = NULL;
    WCHAR      *szFullName = NULL;
    UINT        count;
    WCHAR       szUsn[20];
    LPOLESTR   *pszPrioritySourceList = NULL;
    ADS_ATTR_INFO pAttr[2];
    DWORD       cAttr = 0, cModified = 0, i=0;
    
    if ((!pszPackageName) || IsBadStringPtr(pszPackageName, _MAX_PATH))
        return E_INVALIDARG;

    if ((!pszSourceList) ||
           (!IsValidReadPtrIn(pszSourceList, sizeof(LPOLESTR) * cSources)))
        return E_INVALIDARG;
    
    for (count = 0; (count < cSources); count++) 
        if ((!pszSourceList[count]) || (IsBadStringPtr(pszSourceList[count], _MAX_PATH)))
            return E_INVALIDARG;

    // Construct the Name of the Package Object.
    GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;
       
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
                          &hADs);
    ERROR_ON_FAILURE(hr);

    // Local variable for adding the order to the list.
    pszPrioritySourceList = (LPOLESTR *)CoTaskMemAlloc(cSources * sizeof(LPOLESTR));
    if (!pszPrioritySourceList) 
    {
        hr = E_OUTOFMEMORY;
        ERROR_ON_FAILURE(hr);
    }

    // add the order to the list
    for (count = 0; (count < cSources); count++) 
    {
        pszPrioritySourceList[count] = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*(wcslen(pszSourceList[count])+
                                                    1+1+1+NumDigits10(cSources)));
        
        if (!(pszPrioritySourceList[count])) 
        {
            FREEARR(pszPrioritySourceList, count);
            ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
        }
        
        wsprintf(pszPrioritySourceList[count], L"%d:%s", count, pszSourceList[count]);
    }

    //
    // Update the TimeStamp
    //
    GetCurrentUsn(szUsn);
    
    PackStrToAttr(pAttr+cAttr, PKGUSN, szUsn);
    cAttr++;

    // Pack it into Attribute Structure.
    PackStrArrToAttr(pAttr+cAttr, MSIFILELIST, pszPrioritySourceList, cSources);
    cAttr++;
    
    // Set the Attribute
    hr = ADSISetObjectAttributes(hADs, pAttr, cAttr,  &cModified);
    if (SUCCEEDED(hr))
    {
        //
        // Update Store Usn
        //
        UpdateStoreUsn(m_ADsContainer, szUsn);
    }
    
Error_Cleanup:
    if (hADs)
        ADSICloseDSObject(hADs);
    
    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
   
    if (szFullName)
        CoTaskMemFree(szFullName);

    return RemapErrorCode(hr, m_szContainerName);
}

//---------------------------------------------------------------
//  Function:   ChangePackageUpgradeList
//
//  Synopsis:   Changes the priority of a Package corresp. to
//              a file Extension.
//
//  Arguments:
//  [in]    
//      pszPackageName
//              Package Name to identify the package.
//      cSources
//              Number of sources
//      pszSourceList
//              List of sources
//              
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//  Binds to the Package Object, Makes the new sourcelist with the order
// maintained.
//----------------------------------------------------------------
HRESULT CClassContainer::ChangePackageUpgradeList(
                                                LPOLESTR     pszPackageName,
                                                UINT         cUpgrades,
                                                UPGRADEINFO *prgUpgradeInfoList
                                                )
{
    HRESULT     hr = S_OK;
    HANDLE      hADs = NULL;
    WCHAR      *szFullName = NULL;
    UINT        count = 0, count1 = 0, count2 = 0;
    LPOLESTR   *pProp = NULL, pAttrNames[2] = {UPGRADESPACKAGES, OBJECTGUID}, *rpszUpgrades = NULL;
    ADS_ATTR_INFO pAttr[2], *pAttrGot = NULL;
    DWORD       cAttr = 0, cModified = 0, i=0, posn = 0, cUpgradeInfoStored = 0,
                cAddList = 0, cRemoveList = 0, cgot = 0;
    GUID        PkgGuid;
    WCHAR       szUsn[20];
    UPGRADEINFO *pUpgradeInfoStored = NULL, *pAddList = NULL, *pRemoveList = NULL;
    
    if ((!pszPackageName) || IsBadStringPtr(pszPackageName, _MAX_PATH))
        return E_INVALIDARG;

    if ((cUpgrades) && ((!prgUpgradeInfoList) ||
           (!IsValidReadPtrIn(prgUpgradeInfoList, sizeof(UPGRADEINFO) * cUpgrades))))
        return E_INVALIDARG;

    for (count = 0; (count < cUpgrades); count++)
    {
        if ((!(prgUpgradeInfoList[count].szClassStore)) || 
            IsBadStringPtr((prgUpgradeInfoList[count].szClassStore), _MAX_PATH))
            return E_INVALIDARG;

        if (IsNullGuid(prgUpgradeInfoList[count].PackageGuid))
            return E_INVALIDARG;

        if (((prgUpgradeInfoList[count].Flag & UPGFLG_Uninstall) == 0) &&
            ((prgUpgradeInfoList[count].Flag & UPGFLG_NoUninstall) == 0) &&
            ((prgUpgradeInfoList[count].Flag & UPGFLG_UpgradedBy) == 0))
            return E_INVALIDARG;      
    }    

    // Construct the Name of the Package Object.
    hr = GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);
    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;
    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
                          &hADs);
    ERROR_ON_FAILURE(hr);
   
    // get the guid and upgrade info
    hr = ADSIGetObjectAttributes(hADs, pAttrNames, 2,  &pAttrGot,  &cgot);    
    
    // Package guid
    posn = GetPropertyFromAttr(pAttrGot, cgot, OBJECTGUID);
    if (posn < cgot)
        UnpackGUIDFrom(pAttrGot[posn], &PkgGuid);

    // Upgrade package
    posn = GetPropertyFromAttr(pAttrGot, cgot, UPGRADESPACKAGES);
    if (posn < cgot)
        UnpackStrArrFrom(pAttrGot[posn], &pProp, &cUpgradeInfoStored);

    // allocating the lists
    pUpgradeInfoStored = (UPGRADEINFO *)CoTaskMemAlloc(sizeof(UPGRADEINFO)*(cUpgradeInfoStored));
    pAddList = (UPGRADEINFO *)CoTaskMemAlloc(sizeof(UPGRADEINFO)*(cUpgrades+cUpgradeInfoStored));
    pRemoveList = (UPGRADEINFO *)CoTaskMemAlloc(sizeof(UPGRADEINFO)*(cUpgrades+cUpgradeInfoStored));

    if ((!pUpgradeInfoStored) || (!pAddList) || (!pRemoveList))
        ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);

    // convert the strings to upgradinfo structures.
    for (count = 0; count < (cUpgradeInfoStored); count++)
    {
        WCHAR *pStr = NULL;
        LPOLESTR ptr = pProp[count];
        UINT len = wcslen (ptr);
            
        pUpgradeInfoStored[count].szClassStore = pProp[count];

        if (len <= 41)
            continue;

        *(ptr + len - 3) = NULL;
        pUpgradeInfoStored[count].Flag = wcstoul(ptr+(len-2), &pStr, 16);

        *(ptr + len - 3 - 36 - 2) = L'\0';
        /*      -GUID-'::'*/
        GUIDFromString(ptr+len-3-36, &(pUpgradeInfoStored[count].PackageGuid));        
    }

    cUpgradeInfoStored = count; // we might have skipped some.

    // AddList formed.
    for (count = 0; count < cUpgrades; count++)
    {
        for (count1 = 0; count1 < cUpgradeInfoStored; count1++)
        {
            // ignore flag changes
            if ((wcscmp(pUpgradeInfoStored[count1].szClassStore, prgUpgradeInfoList[count].szClassStore) == 0) && 
                (memcmp(&pUpgradeInfoStored[count1].PackageGuid, &prgUpgradeInfoList[count].PackageGuid, sizeof(GUID)) == 0))
                break;
        }

        if (count1 == cUpgradeInfoStored)
            pAddList[cAddList++] = prgUpgradeInfoList[count];
    }

    // remove list formed.
    for (count1 = 0; count1 < cUpgradeInfoStored; count1++)
    {
        for (count = 0; count < cUpgrades; count++)
        {
            // ignore flag changes
            if ((wcscmp(pUpgradeInfoStored[count1].szClassStore, prgUpgradeInfoList[count].szClassStore) == 0) && 
                (memcmp(&pUpgradeInfoStored[count1].PackageGuid, &prgUpgradeInfoList[count].PackageGuid, sizeof(GUID)) == 0))
                break;
        }

        if (count == cUpgrades)
            pRemoveList[cRemoveList++] = pUpgradeInfoStored[count];
    }

    for (count = 0; count < cAddList; count++)
    {
        // in case of UpgradedBy do no try to fix up the links.
        if (!(pAddList[count].Flag & UPGFLG_UpgradedBy))
        {            
            DWORD   Flags = 0;
            if (pAddList[count].Flag & UPGFLG_Enforced)
                Flags = UPGFLG_Enforced;
        }
    }

    for (count = 0; count < cRemoveList; count++)
    {
        // in case of UpgradedBy do no try to fix up the links.
        if (!(pRemoveList[count].Flag  & UPGFLG_UpgradedBy))
        {            
            DWORD   Flags = 0;
            if (pRemoveList[count].Flag & UPGFLG_Enforced)
                Flags = UPGFLG_Enforced;
        }
    }

    rpszUpgrades = (LPOLESTR *)CoTaskMemAlloc(sizeof(LPOLESTR)*cUpgrades);
    if (!rpszUpgrades)
        ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);

    for (count = 0; (count < cUpgrades); count++) 
    {
        WCHAR szPackageGuid[_MAX_PATH];
        UINT len = wcslen(prgUpgradeInfoList[count].szClassStore);

        rpszUpgrades[count] = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) *(36+PKG_UPG_DELIM1_LEN+len+PKG_UPG_DELIM2_LEN+2+2));
                                                           // Guid size+::+length++:+flagDigit+2 
        if (!rpszUpgrades[count])
        {
            FREEARR(rpszUpgrades, count);  
            ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
        }

        StringFromGUID(prgUpgradeInfoList[count].PackageGuid, szPackageGuid);
        wsprintf(rpszUpgrades[count], L"%s%s%s%s%02x", prgUpgradeInfoList[count].szClassStore, PKG_UPG_DELIMITER1, szPackageGuid,
                        PKG_UPG_DELIMITER2, prgUpgradeInfoList[count].Flag%16);
    }

    PackStrArrToAttr(pAttr+cAttr, UPGRADESPACKAGES, rpszUpgrades, cUpgrades); 
    cAttr++;

    //
    // Update the TimeStamp
    //
    GetCurrentUsn(szUsn);
    
    PackStrToAttr(pAttr+cAttr, PKGUSN, szUsn);
    cAttr++;
    
    // Set the Attribute
    hr = ADSISetObjectAttributes(hADs, pAttr, cAttr,  &cModified);
    if (SUCCEEDED(hr))
    {
        //
        // Update Store Usn
        //
        UpdateStoreUsn(m_ADsContainer, szUsn);
    }
    
Error_Cleanup:
    if (hADs)
        ADSICloseDSObject(hADs);

    if (szFullName)
        CoTaskMemFree(szFullName);
    
    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);

    if (pAttrGot)
        FreeADsMem(pAttrGot);

    if (pProp)
        CoTaskMemFree(pProp);

    if (pUpgradeInfoStored)
        CoTaskMemFree(pUpgradeInfoStored);
    
    if (pAddList) 
        CoTaskMemFree(pAddList);
    
    if (pRemoveList)
        CoTaskMemFree(pRemoveList);

    return RemapErrorCode(hr, m_szContainerName);
}


extern LPOLESTR szAppCategoryColumns;

//---------------------------------------------------------------
//  Function:   GetAppCategories
//
//  Synopsis:   gets the list of Package Categories in the Domain.
//
//  Arguments:
//  [in]    
//      Locale
//              Locale for the categories. Used to get the description.
//  [out]
//      pAppCategoryList
//              the list of Application Categories in the domain
//            
//  Returns:
//      S_OK, E_OUTOFMEMORY, CS_E_XXX
//
//  Gets the FullName of the Domain, binds to the AppCategory container
//  below that. and gets all the categories with approp. types.
//----------------------------------------------------------------

HRESULT CClassContainer::GetAppCategories (
                                           LCID                  Locale,
                                           APPCATEGORYINFOLIST  *pAppCategoryList
                                           )
{
    HRESULT             hr = S_OK;
    WCHAR               szfilter[_MAX_PATH];
    WCHAR               szRootPath[_MAX_PATH], szAppCategoryContainer[_MAX_PATH];
    HANDLE              hADs = NULL;
    ADS_SEARCH_HANDLE   hADsSearchHandle = NULL;
    ADS_SEARCHPREF_INFO SearchPrefs[2];
    
    // set the search preference.
    SearchPrefs[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
    SearchPrefs[0].vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs[0].vValue.Integer = ADS_SCOPE_ONELEVEL;
    
    // we do not expect too many categories
    SearchPrefs[1].dwSearchPref = ADS_SEARCHPREF_PAGESIZE;
    SearchPrefs[1].vValue.dwType = ADSTYPE_INTEGER;
    SearchPrefs[1].vValue.Integer = 20;
    
    if (!IsValidPtrOut(pAppCategoryList, sizeof(APPCATEGORYINFOLIST)))
        return E_INVALIDARG;
    
    // get the name of the domain.
    hr = GetRootPath(szRootPath);
    ERROR_ON_FAILURE(hr);
    
    // Names returned by GetRootPath are in only 1 format and we don't need to
    // use BuildADsPath.
    wsprintf(szAppCategoryContainer, L"%s%s%s", LDAPPREFIX, APPCATEGORYCONTAINERNAME, 
                                                        szRootPath+LDAPPREFIXLENGTH);
    
    wsprintf(szfilter, L"(objectClass=%s)", CLASS_CS_CATEGORY);
    
    //binds to the category container
    hr = ADSIOpenDSObject(szAppCategoryContainer, NULL, NULL, 
                                                ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, &hADs);
    ERROR_ON_FAILURE(hr);
    
    hr = ADSISetSearchPreference(hADs, SearchPrefs, 2);
    ERROR_ON_FAILURE(hr);
    
    // gets a search handle
    hr = ADSIExecuteSearch(hADs, szfilter, pszCategoryAttrNames, cCategoryAttr, &hADsSearchHandle);
    ERROR_ON_FAILURE(hr);
    
    // tries to find out the number of categories.
    pAppCategoryList->cCategory = 0;
    for (hr = ADSIGetFirstRow(hADs, hADsSearchHandle);
                       ((SUCCEEDED(hr)) && (hr != S_ADS_NOMORE_ROWS));
	               hr = ADSIGetNextRow(hADs, hADsSearchHandle))        
        pAppCategoryList->cCategory++;
    
    // get the number of elements.    
    pAppCategoryList->pCategoryInfo = (APPCATEGORYINFO *)CoTaskMemAlloc(
                                                      sizeof(APPCATEGORYINFO)*
                                                      pAppCategoryList->cCategory);
    
    if (!(pAppCategoryList->pCategoryInfo))
    {
        pAppCategoryList->cCategory = 0;
        ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
    }
   
    // if it has come till here, it has to have a search handle
    ADSICloseSearchHandle(hADs, hADsSearchHandle);    
    hADsSearchHandle = NULL;

    memset(pAppCategoryList->pCategoryInfo, 0, sizeof(APPCATEGORYINFO)*pAppCategoryList->cCategory);

    // gets a search handle
    hr = ADSIExecuteSearch(hADs, szfilter, pszCategoryAttrNames, cCategoryAttr, &hADsSearchHandle);
    ERROR_ON_FAILURE(hr);

    // passes the search handle and gets the categorylist.
    hr = FetchCategory(hADs, hADsSearchHandle, pAppCategoryList, Locale);
    ERROR_ON_FAILURE(hr);
    
Error_Cleanup:
    
    if (hADsSearchHandle)
        ADSICloseSearchHandle(hADs, hADsSearchHandle);

    if (hADs)
        ADSICloseDSObject(hADs);
    return RemapErrorCode(hr, m_szContainerName);
}

//---------------------------------------------------------------
//  Function:   RegisterAppCategory
//
//  Synopsis:   Adda category and assoc desc. for the whole Domain(This is per domain
//              and not per class store.)
//
//  Arguments:
//  [in]    
//      pAppCategory
//              Pointer to a APPCATEGORYINFO structure to be added.
//
//  Returns:
//      S_OK, E_OUTOFMEMORY, E_INVALIDARG, CS_E_XXX
//
//  Finds the root path of the domain. binds to the category container
//  underneath it. deletes this particular AppCategory.
//----------------------------------------------------------------
HRESULT CClassContainer::RegisterAppCategory (
                                              APPCATEGORYINFO    *pAppCategory
                                              )
{
    WCHAR           szRootPath[_MAX_PATH], localedescription[128+16],
                    szAppCategoryContainer[_MAX_PATH], szRDN[_MAX_PATH],
                  * szFullName = NULL, szAppCatid[_MAX_PATH];

    HRESULT         hr = S_OK;
    HANDLE          hADsContainer = NULL, hADs = NULL;
    ULONG           i, j, cdesc = 0, posn = 0;
    LPOLESTR      * pszDescExisting = NULL, pszDesc = NULL;
    LPOLESTR        AttrName = LOCALEDESCRIPTION;
    ADS_ATTR_INFO * pAttrGot = NULL, pAttr[6];
    DWORD           cgot = 0, cAttr = 0;
    BOOL            fExists = TRUE;
    
    if ((!pAppCategory) || (!IsValidReadPtrIn(pAppCategory, sizeof(APPCATEGORYINFO))))
        return E_INVALIDARG;
    
    if ((pAppCategory->pszDescription == NULL) || 
        (IsBadStringPtr(pAppCategory->pszDescription, _MAX_PATH)))
        return E_INVALIDARG;
    
    if (IsNullGuid(pAppCategory->AppCategoryId))
        return E_INVALIDARG;
    
    // get the name of the root of the domain
    hr = GetRootPath(szRootPath);
    ERROR_ON_FAILURE(hr);
    
    // Bind to a AppCategory container
    
    // Names returned by GetRootPath are in only 1 format and we don't need to
    // use BuildADsPath.
    
    wsprintf(szAppCategoryContainer, L"%s%s%s", LDAPPREFIX, APPCATEGORYCONTAINERNAME, 
        szRootPath+LDAPPREFIXLENGTH);
    
    // container is supposed to exist.
    hr = ADSIOpenDSObject(szAppCategoryContainer, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
                          &hADsContainer);
    ERROR_ON_FAILURE(hr);


    RDNFromGUID(pAppCategory->AppCategoryId, szRDN);
    
    wsprintf(localedescription, L"%x %s %s", pAppCategory->Locale, CAT_DESC_DELIMITER,
        pAppCategory->pszDescription);
    
    BuildADsPathFromParent(szAppCategoryContainer, szRDN, &szFullName);
    
    // BUGBUG:: ADS_FAST_BIND and the create seems to be going twice. why?
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, &hADs);
    
    if (SUCCEEDED(hr))
        hr = ADSIGetObjectAttributes(hADs, &AttrName, 1, &pAttrGot, &cgot);

    if (SUCCEEDED(hr))
    {
        fExists = TRUE;
    }
    else 
    {
        fExists = FALSE;
        PackStrToAttr(pAttr, OBJECTCLASS, CLASS_CS_CATEGORY); cAttr++;
        
        PackGUIDToAttr(pAttr+cAttr, CATEGORYCATID, &(pAppCategory->AppCategoryId)); cAttr++;
        
        hr = ADSICreateDSObject(hADsContainer, szRDN, pAttr, cAttr);
        
        for (j = 0; j < cAttr; j++)
            FreeAttr(pAttr[j]);
        cAttr = 0;
        
        if (hADs)
        {
            ADSICloseDSObject(hADs);
            hADs = NULL;
        }
        hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, &hADs);
    }
    
    if (szFullName)
        FreeADsMem(szFullName);
    
    ERROR_ON_FAILURE(hr);
    
    if (fExists) {
        if (cgot) 
        {
            UnpackStrArrFrom(pAttrGot[0], &pszDescExisting, &cdesc);
        }
        
        // Existing list of descriptions
        if (posn = FindDescription(pszDescExisting, cdesc, &(pAppCategory->Locale), NULL, 0))
        {   // Delete the old value
            PackStrArrToAttrEx(pAttr+cAttr, LOCALEDESCRIPTION, pszDescExisting+(posn-1), 1, FALSE); cAttr++;
        }
        CoTaskMemFree(pszDescExisting);
    }
    
    pszDesc = localedescription;
    
    PackStrArrToAttrEx(pAttr+cAttr, LOCALEDESCRIPTION, &pszDesc, 1, TRUE);
    cAttr++;
    
    DWORD cModified;
    hr = ADSISetObjectAttributes(hADs, pAttr, cAttr, &cModified);
    
Error_Cleanup:
    
    if (pAttrGot)
        FreeADsMem(pAttrGot);
    
    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
    
    if (hADs)
        ADSICloseDSObject(hADs);
    
    if (hADsContainer)
        ADSICloseDSObject(hADsContainer);
    
    return RemapErrorCode(hr, m_szContainerName);
}



//---------------------------------------------------------------
//  Function:   UnregisterAppCategory
//
//  Synopsis:   Removes a category from the whole Domain(This is per domain)
//              and not per class store.
//
//  Arguments:
//  [in]    
//      pAppCategoryId
//              Pointer to a GUID that has to be removed.
//
//  Returns:
//      S_OK, E_OUTOFMEMORY, E_INVALIDARG, CS_E_XXX
//
//  Finds the root path of the domain. binds to the category container
//  underneath it. deletes this particular AppCategory.
//----------------------------------------------------------------
HRESULT CClassContainer::UnregisterAppCategory (
                                                GUID         *pAppCategoryId
                                                )
{
    WCHAR           szRootPath[_MAX_PATH], szRDN[_MAX_PATH],
                    szAppCategoryContainer[_MAX_PATH];
    HRESULT         hr = S_OK;
    HANDLE          hADs = NULL;
    
    if (!IsValidReadPtrIn(pAppCategoryId, sizeof(GUID)))
        return E_INVALIDARG;
    
    hr = GetRootPath(szRootPath);
    // Bind to a AppCategory container
    
    // Names returned by GetRootPath are in only 1 format and we don't need to
    // use BuildADsPath.
    
    wsprintf(szAppCategoryContainer, L"%s%s%s", LDAPPREFIX, APPCATEGORYCONTAINERNAME, 
        szRootPath+LDAPPREFIXLENGTH);
    
    hr = ADSIOpenDSObject(szAppCategoryContainer, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
                            &hADs);
    
    ERROR_ON_FAILURE(hr);
    
    RDNFromGUID(*pAppCategoryId, szRDN);
    
    hr = ADSIDeleteDSObject(hADs, szRDN);
    
    ADSICloseDSObject(hADs);
    
    // Delete this category
    
Error_Cleanup:
    return RemapErrorCode(hr, m_szContainerName);
}


//---------------------------------------------------------------
//  Function:   DeletePackage
//
//  Synopsis:   Permanently remove a package and the associated Classes 
//              from class store
//
//  Arguments:
//  [in]    
//      PackageGuid
//              Guid of the package that has to be removed.
//
//  Returns:
//      S_OK, E_OUTOFMEMORY, E_INVALIDARG, CS_E_XXX
//
//  Deletes the package and all the clsids associated with the 
//  package (using DeleteClass) Ignores the error from DeleteClass
//  Tries to delete all the upgrade relationships from this package.
//  Errors are ignored.
//----------------------------------------------------------------
HRESULT CClassContainer::DeletePackage (LPOLESTR    szFullName
                                        )
{
    HRESULT         hr = S_OK;
    DWORD           cStr = 0, count = 0, cgot = 0, posn = 0;
    LPOLESTR        szRDN = NULL, szJunk = NULL;
    LPOLESTR      * szStr = NULL;
    LPOLESTR        pAttrName[] = {PKGCLSIDLIST, UPGRADESPACKAGES, OBJECTGUID};
    ADS_ATTR_INFO * pAttr = NULL;
    HANDLE          hADs = NULL;
    WCHAR           szUsn[20];
    GUID            PackageGuid;

    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND,
                            &hADs);
    
    if (!SUCCEEDED(hr))
        return hr;
    
    GetCurrentUsn(szUsn);
    
    hr = ADSIGetObjectAttributes(hADs, pAttrName, 3, &pAttr, &cgot);

    memset(&PackageGuid, 0, sizeof(GUID));
    posn = GetPropertyFromAttr(pAttr, cgot,  OBJECTGUID);
    if (posn < cgot)
        UnpackGUIDFrom(pAttr[posn], &PackageGuid);

    posn = GetPropertyFromAttr(pAttr, cgot,  PKGCLSIDLIST);
    if (posn < cgot)
        UnpackStrArrFrom(pAttr[posn], &szStr, &cStr);

    for (count = 0; count < cStr; count++)
    {
        if (wcslen(szStr[count]) > (STRINGGUIDLEN-1))
            szStr[count][STRINGGUIDLEN-1] = L'\0';
        hr = DeleteClass(szStr[count]);
    }

    if (szStr)
        CoTaskMemFree(szStr);
    szStr = NULL;
    cStr = 0;

    posn = GetPropertyFromAttr(pAttr, cgot, UPGRADESPACKAGES);
    if (posn < cgot)
        UnpackStrArrFrom(pAttr[posn],  &szStr, &cStr);

    for (count = 0; count < cStr; count++)
    {
        GUID        UpgradeeGuid;
        WCHAR      *pStr = NULL;
        LPOLESTR    ptr = szStr[count];
        UINT        len = wcslen (ptr);
        DWORD       UpgradeFlag, Flags = 0;

        if (len <= 41)
            continue;

        *(ptr + (len - 3)) = NULL;
                    
        UpgradeFlag = wcstoul(ptr+(len-2), &pStr, 16);

        *(ptr + (len - 3 - 36 - 2)) = L'\0';
                 /*      -GUID-'::'*/
        GUIDFromString(ptr+len-3-36, &UpgradeeGuid);
    
        if (UpgradeFlag & UPGFLG_Enforced)
            Flags = UPGFLG_Enforced;

    }
    
    if (szStr)
        CoTaskMemFree(szStr);
    szStr = NULL;
    cStr = 0;

    // ignore errors
    if (pAttr)
        FreeADsMem(pAttr);
    
    ADSICloseDSObject(hADs);
    
    BuildADsParentPath(szFullName, &szJunk, &szRDN);
    
    if (szJunk)
        FreeADsMem(szJunk);

    hr = ADSIDeleteDSObject(m_ADsPackageContainer, szRDN);
    if (szRDN)
        FreeADsMem(szRDN);

    if (SUCCEEDED(hr))
    {
        //
        // Update Store Usn
        //
        UpdateStoreUsn(m_ADsContainer, szUsn);
    }
    return hr;
}

//---------------------------------------------------------------
//  Function:   RemovePackage
//
//  Synopsis:   Mark a package as disabled or orphaned
//              Or permanently remove a package and the associated Classes 
//              from class store
//
//  Arguments:
//  [in]    
//      PackageGuid
//              Guid of the package that has to be removed.
//  [in]
//      dwFlags
//              The new flags for the package. To delete the package explicitly
//              use flag zero or orphan.
//            
//  Returns:
//      S_OK, E_OUTOFMEMORY, E_INVALIDARG, CS_E_XXX
//
//  Calls Delete package if the flags is zero or Orphan.
//  Otherwise it sets the new flags and stamps the new time stamp.
//----------------------------------------------------------------
HRESULT CClassContainer::RemovePackage (
                                        LPOLESTR       pszPackageName,
                                        DWORD          dwFlags
                                        )
{
    HRESULT         hr = S_OK;
    WCHAR          *szRDN = NULL, *szFullName = NULL;
    HANDLE          hADs = NULL;
    WCHAR           szUsn[20];
    ADS_ATTR_INFO   pAttr[7];
    DWORD           cAttr = 0, cModified = 0, i=0;

    if ((dwFlags != 0) && (dwFlags != ACTFLG_Orphan) && (dwFlags != ACTFLG_Uninstall))
        return E_INVALIDARG;
    
    hr = GetDNFromPackageName(pszPackageName, &szFullName);
    ERROR_ON_FAILURE(hr);

    if (hr != S_OK)
        return CS_E_OBJECT_NOTFOUND;

    if (dwFlags == 0)
        // delete the package from the class store
    {
        hr = DeletePackage(szFullName);
    }
    else
    {
        GUID    NewPackageId;
        WCHAR   szNewRDN[_MAX_PATH], *szRDN = NULL, *szJunk = NULL;
        //
        // PackageName is unchanged.
        //
        GetCurrentUsn(szUsn);

/*
        CoCreateGuid(&NewPackageId);
        RDNFromGUID(NewPackageId, szNewRDN);

        BuildADsParentPath(szFullName, &szJunk, &szRDN);
        if (szJunk)
            FreeADsMem(szJunk);

        hr = ADSIModifyRdn(m_ADsPackageContainer, szRDN, szNewRDN);
        if (szRDN)
            FreeADsMem(szRDN);

        ERROR_ON_FAILURE(hr);
        
        // construct the Full Path for the Package.
        BuildADsPathFromParent(m_szPackageName, szNewRDN, &szFullName);
*/        
        // Bind to the Package Object.
        hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, &hADs);
        ERROR_ON_FAILURE(hr);

        // setting the flag as orphan/uninstall
        PackDWToAttr (pAttr+cAttr, PACKAGEFLAGS, dwFlags);
        cAttr++;

        // stamping the modification time for cleanup later.
        PackStrToAttr (pAttr+cAttr, PKGUSN, szUsn);
        cAttr++;

        hr = ADSISetObjectAttributes(hADs, pAttr, cAttr, &cModified);

        if (hADs)
            ADSICloseDSObject(hADs);

        for (i = 0; i < cAttr; i++)
            FreeAttr(pAttr[i]);
    
        if (SUCCEEDED(hr))
        {
            //
            // Update Class Store Usn
            //
            UpdateStoreUsn(m_ADsContainer, szUsn);
        }
    }
        
Error_Cleanup:
    if (szFullName)
        CoTaskMemFree(szFullName);

    return RemapErrorCode(hr, m_szContainerName);
}

// Merges list1 and List2 into ResList removing duplicates.
HRESULT MergePropList(LPOLESTR    *List1,  DWORD   cList1,
                   LPOLESTR    *List2,  DWORD   cList2,
                   LPOLESTR   **ResList,DWORD  *cResList)
{
    DWORD i, j;
    
    *cResList = 0;
    *ResList = (LPOLESTR *)CoTaskMemAlloc(sizeof(LPOLESTR)*(cList1+cList2));
    if (!*ResList)
        return E_OUTOFMEMORY;

    for (i = 0; i < cList1; i++)
        (*ResList)[i] = List1[i];
    
    for (i = 0; i < cList2; i++) {
        for (j = 0; j < cList1; j++)
            if (wcscmp((*ResList)[j], List2[i]) == 0)
                break;
            
            if (j == cList1)
                (*ResList)[(*cResList)++] = List2[i];
    }

    return S_OK;
}

//---------------------------------------------------------------
//  Function:   NewClass
//
//  Synopsis:   Adds classes corresp. to a package under the DS.
//              Called by AddPackage.
//
//  Arguments:
//  [in]    
//      pClassDetail
//              Class Detail of the clsid that needs to be added.
//              Validation is specified in class store doc.
//            
//  Returns:
//      S_OK, E_OUTOFMEMORY, E_INVALIDARG, CS_E_XXX
//
//  Validates the classdetail structure. If the clsid already exists in
//  the DS, it adds these values under the same clsid and increases the refcount.
//----------------------------------------------------------------
HRESULT CClassContainer::NewClass (CLASSDETAIL *pClassDetail)
{
    HRESULT         hr = S_OK;
    STRINGGUID      szGUID1, szGUID2;
    WCHAR           szRDN [_MAX_PATH], * szFullName = NULL;
    ADS_ATTR_INFO   pAttr[6], *pAttrsGot = NULL;
    BOOL            fExists = FALSE;
    HANDLE          hADs = NULL;
    LPOLESTR        AttrNames[] = {PKGFILEEXTNLIST, PROGIDLIST, CLASSREFCOUNTER};
    DWORD           posn = 0, cProp = 0, cPropMerged = 0, refcount = 0,
                    cAttr = 0, cModified = 0, cgot = 0, i;
    LPOLESTR      * pszProp = NULL, *pszPropMerged = NULL;
    
    if (!m_fOpen)
        return E_FAIL;
    
    //
    // Cant be a NULL guid
    //
    
    if (IsNullGuid(pClassDetail->Clsid))
        return E_INVALIDARG;

    // Not using RDNFrom GUID b'cos szGUID1 is used below
    StringFromGUID(pClassDetail->Clsid, szGUID1);    
    wsprintf(szRDN, L"CN=%s", szGUID1);
    
    BuildADsPathFromParent(m_szClassName, szRDN, &szFullName);
    
    hr = ADSIOpenDSObject(szFullName, NULL, NULL, ADS_SECURE_AUTHENTICATION,
        &hADs);
    
    if (SUCCEEDED(hr)) {
        fExists = TRUE;
        hr = ADSIGetObjectAttributes(hADs, AttrNames, 3, &pAttrsGot, &cgot);
    }
    
    // BUGBUG :: bug in adsldpc doesn't return error in open.
    if (!SUCCEEDED(hr))
        fExists = FALSE;
    
    //
    // Create the RDN for the Class Object
    //
    
    if (!fExists) {
        PackStrToAttr(pAttr+cAttr, OBJECTCLASS, CLASS_CS_CLASS); cAttr++;
        PackStrToAttr(pAttr+cAttr, CLASSCLSID, szGUID1); cAttr++;
    }
    
    if (pClassDetail->cProgId)
    {
        if (fExists)
            posn = GetPropertyFromAttr(pAttrsGot, cgot, PROGIDLIST);
        
        if (posn < cgot)
            UnpackStrArrFrom(pAttrsGot[posn], &pszProp, &cProp);
        
        // we can not just append b'cos duplicate values are valid conditions.
        
        MergePropList(pszProp, cProp,
            pClassDetail->prgProgId, pClassDetail->cProgId,
            &pszPropMerged, &cPropMerged);
        
        PackStrArrToAttr(pAttr+cAttr, PROGIDLIST, pszPropMerged, cPropMerged);
        cAttr++;
        
        CoTaskMemFree(pszPropMerged);
        pszPropMerged = NULL; cPropMerged = 0;
        
        CoTaskMemFree(pszProp);
        pszProp = NULL; cProp = 0;
    }
    
    if (!IsNullGuid(pClassDetail->TreatAs))
    {
        StringFromGUID(pClassDetail->TreatAs, szGUID2);
        PackStrToAttr(pAttr+cAttr, TREATASCLSID, szGUID2);
        cAttr++;
    }
    
    // this is going to be modified if find that an entry already exists.
    
    if (fExists) {
        posn = GetPropertyFromAttr(pAttrsGot, cgot, CLASSREFCOUNTER);
        if (posn < cgot) {
            UnpackDWFrom(pAttrsGot[posn], &refcount);
        }
    }
    
    refcount++;
    PackDWToAttr(pAttr+cAttr, CLASSREFCOUNTER, refcount);
    cAttr++;
    
    if (fExists)
        hr = ADSISetObjectAttributes(hADs, pAttr, cAttr, &cModified);
    else
        hr = ADSICreateDSObject(m_ADsClassContainer, szRDN, pAttr, cAttr);
    
    if (pAttrsGot)
        FreeADsMem(pAttrsGot);
    
    if (szFullName)
        FreeADsMem(szFullName);
    
    for (i = 0; i < cAttr; i++)
        FreeAttr(pAttr[i]);
    
    return RemapErrorCode(hr, m_szContainerName);
}

#define SCRIPT_IN_DIRECTORY    256

//---------------------------------------------------------------
//  Function:   AddPackage
//
//  Synopsis:   Adds a package object in the DS.
//
//  Arguments:
//  [out]    
//      pszPackageId
//              An Id that is returned corresponding to the package.
//  [in]
//      pPackageDetail
//              Pointer to a PACKAGEDETAIL info for this package
//              The various validations that is done is documented
//              in associated class store doc.
//            
//  Returns:
//      S_OK, E_OUTOFMEMORY, E_INVALIDARG, CS_E_XXX
//
//  Validates the packagedetail structure. Packs ADS_ATTR_INFO structure with
//  the values and tries to create the object in the DS.
//  Calls NewClass to add all the clsids after that. 
//  If this returns error
//                     the whole package is removed.
//----------------------------------------------------------------
HRESULT CClassContainer::AddPackage (
                                     PACKAGEDETAIL *pPackageDetail,
                                     GUID          *pPkgGuid
                                     )
{
    HRESULT             hr = S_OK;
    WCHAR               szRDN [_MAX_PATH];
    LPOLESTR          * pszGuid1 = NULL, *pszGuid2 = NULL,
                      * pszGuid3 = NULL, *pszGuid4 = NULL,
                      * pszProgId = NULL, *pszFileExt = NULL,
                      * rpszUpgrades = NULL, *rpszSources = NULL,
                        szPackageId = NULL, szJunk = NULL;

    DWORD             * pdwArch=NULL, count = 0, cPackProgId = 0;
    ADS_ATTR_INFO       pAttr[29]; 
    DWORD               cAttr = 0;
    WCHAR               szUsn[20];
    BOOL                fPackageCreated = FALSE, GenerateGuid = FALSE;
    GUID                PackageGuidId;
        
    if ((!pPkgGuid) || !IsValidReadPtrIn(pPkgGuid, sizeof(GUID)))
        return E_INVALIDARG;

    if ((!(pPackageDetail->pszPackageName)) || 
                IsBadStringPtr((pPackageDetail->pszPackageName), _MAX_PATH))
        return E_INVALIDARG;


    if (!pPackageDetail)
        return E_INVALIDARG;
    
    if (!IsValidReadPtrIn(pPackageDetail, sizeof(PACKAGEDETAIL)))
        return E_INVALIDARG;
    
    // validating ActivationInfo.
    if (pPackageDetail->pActInfo)
    {    
        if (!IsValidReadPtrIn(pPackageDetail->pActInfo, sizeof(ACTIVATIONINFO)))
            return E_INVALIDARG;
        
        if (!IsValidReadPtrIn(pPackageDetail->pActInfo->pClasses,
            sizeof(CLASSDETAIL) * (pPackageDetail->pActInfo->cClasses)))
            return E_INVALIDARG;

        // validating classdetail
        for (count = 0; (count < (pPackageDetail->pActInfo->cClasses)); count++)
        {
            CLASSDETAIL *pClassDetail = (pPackageDetail->pActInfo->pClasses)+count;
            if (IsNullGuid(pClassDetail->Clsid))
               return E_INVALIDARG;

            for (DWORD count1 = 0; (count1 < (pClassDetail->cProgId)); count1++)
            {
                // if profid is NULL or an empty string.
                if ((!((pClassDetail->prgProgId)[count1])) || 
                    (!((pClassDetail->prgProgId)[count1][0])))
                    return E_INVALIDARG;
            }
        }
        
        if (!IsValidReadPtrIn(pPackageDetail->pActInfo->prgShellFileExt,
            sizeof(LPOLESTR) * (pPackageDetail->pActInfo->cShellFileExt)))
            return E_INVALIDARG;
        
        for (count = 0; count < (pPackageDetail->pActInfo->cShellFileExt); count++)
        {
            if (!pPackageDetail->pActInfo->prgShellFileExt[count])
                return E_INVALIDARG;
        }
        
        if (!IsValidReadPtrIn(pPackageDetail->pActInfo->prgPriority,
            sizeof(UINT) * (pPackageDetail->pActInfo->cShellFileExt)))
            return E_INVALIDARG;
        
        if (!IsValidReadPtrIn(pPackageDetail->pActInfo->prgInterfaceId,
            sizeof(IID) * (pPackageDetail->pActInfo->cInterfaces)))
            return E_INVALIDARG;
        
        if (!IsValidReadPtrIn(pPackageDetail->pActInfo->prgTlbId,
            sizeof(GUID) * (pPackageDetail->pActInfo->cTypeLib)))
            return E_INVALIDARG;
    }
    
    // Validating InstallInfo
    // BUGBUG:: Validate ProductCode, Mvipc
    if ((pPackageDetail->pInstallInfo == NULL) || 
        (!IsValidReadPtrIn(pPackageDetail->pInstallInfo, sizeof(INSTALLINFO)))
        )
        return E_INVALIDARG;
    
    if (!IsValidReadPtrIn(pPackageDetail->pInstallInfo->prgUpgradeInfoList, 
        sizeof(UPGRADEINFO)*(pPackageDetail->pInstallInfo->cUpgrades)))
        return E_INVALIDARG;
    
    for (count = 0; count < (pPackageDetail->pInstallInfo->cUpgrades); count++)
    {
        if ((!(pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].szClassStore)) || 
            IsBadStringPtr((pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].szClassStore), _MAX_PATH))
            return E_INVALIDARG;

        if (IsNullGuid(pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].PackageGuid))
            return E_INVALIDARG;

        if (((pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag & UPGFLG_Uninstall) == 0) &&
            ((pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag & UPGFLG_NoUninstall) == 0) &&
            ((pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag & UPGFLG_UpgradedBy) == 0))
            return E_INVALIDARG;      
    }    
    
    // validating PlatformInfo    
    
    if ((pPackageDetail->pPlatformInfo == NULL) || 
        (!IsValidReadPtrIn(pPackageDetail->pPlatformInfo, sizeof(PLATFORMINFO)))
        )
        return E_INVALIDARG;
    
    if (!IsValidReadPtrIn(pPackageDetail->pPlatformInfo->prgPlatform,
        sizeof(CSPLATFORM) * (pPackageDetail->pPlatformInfo->cPlatforms)))
        return E_INVALIDARG;
    
    if ((pPackageDetail->pPlatformInfo->cLocales == 0) ||
        (pPackageDetail->pPlatformInfo->cPlatforms == 0))
        return E_INVALIDARG;
    
    if (!IsValidReadPtrIn(pPackageDetail->pPlatformInfo->prgLocale,
        sizeof(LCID) * (pPackageDetail->pPlatformInfo->cLocales)))
        return E_INVALIDARG;
    
    // validating InstallInfo
    
    // Validating other fields in PackageDetail structure
    
    if ((pPackageDetail->pszSourceList == NULL) ||
        (!IsValidReadPtrIn(pPackageDetail->pszSourceList,
        sizeof(LPOLESTR) * (pPackageDetail->cSources))))
        return E_INVALIDARG;
    
    for (count = 0; count < (pPackageDetail->cSources); count++)
    {
        if ((!pPackageDetail->pszSourceList[count]) || 
                        (IsBadStringPtr(pPackageDetail->pszSourceList[count], _MAX_PATH)))
            return E_INVALIDARG;
    }
    
    if (pPackageDetail->rpCategory)
    {
        if (!IsValidReadPtrIn(pPackageDetail->rpCategory,
            sizeof(GUID) * (pPackageDetail->cCategories)))
            return E_INVALIDARG;
    }

    // If the restrictions are too constrictive then we should go to
    // the DS, to see whether it is a valid name or not. till then..

//    GenerateGuid = InvalidDSName(pPackageDetail->pszPackageName);

    hr = GetDNFromPackageName(pPackageDetail->pszPackageName, &szJunk);
    if (szJunk)
        CoTaskMemFree(szJunk);

    if (FAILED(hr))
        return RemapErrorCode(hr, m_szContainerName);

    if (hr == S_OK)
    {
        return CS_E_OBJECT_ALREADY_EXISTS;
    }

/*    szPackageId = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*(GenerateGuid?41:
                                        (wcslen(pPackageDetail->pszPackageName)+1)));
*/

    szPackageId = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*41);

    if (!(szPackageId))
        return E_OUTOFMEMORY;

    memset(&PackageGuidId, 0, sizeof(GUID));
/*
    if (GenerateGuid)
    {
        CoCreateGuid(&PackageGuidId);
        StringFromGUID(PackageGuidId, szPackageId);
    }
    else 
        wcscpy(szPackageId, pPackageDetail->pszPackageName);
*/
    // always generate guid

    CoCreateGuid(&PackageGuidId);
    StringFromGUID(PackageGuidId, szPackageId);

    //
    // Create the RDN for the Package Object
    //

    wsprintf(szRDN, L"CN=%s", szPackageId);

    
    PackStrToAttr(pAttr+cAttr, OBJECTCLASS, CLASS_CS_PACKAGE); cAttr++;
    
    // fill in the activation info
    
    // add the class to the packagecontainer list
    
    if (pPackageDetail->pActInfo)
    {
        if (pPackageDetail->pActInfo->cClasses) 
        {
            pszGuid1 = (LPOLESTR *)CoTaskMemAlloc((pPackageDetail->pActInfo->cClasses)*sizeof(LPOLESTR));
            if (!pszGuid1) {
                ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
            }
            
            for (count = 0; count < pPackageDetail->pActInfo->cClasses; count++) 
            {
                WCHAR   szCtx[9];

                pszGuid1[count] = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*(STRINGGUIDLEN+9));
                if (!pszGuid1[count]) {
                    FREEARR(pszGuid1, count);
                    ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);                                
                }

                StringFromGUID(pPackageDetail->pActInfo->pClasses[count].Clsid, pszGuid1[count]);
                wsprintf(szCtx, L":%8x", pPackageDetail->pActInfo->pClasses[count].dwComClassContext);
                wcscat(pszGuid1[count], szCtx);
                cPackProgId += pPackageDetail->pActInfo->pClasses[count].cProgId;
            }
            
            PackStrArrToAttr(pAttr+cAttr, PKGCLSIDLIST, pszGuid1,
                pPackageDetail->pActInfo->cClasses); cAttr++; 
        }
        
        // collecting all the progids from the various clsids.
        pszProgId = (LPOLESTR *)CoTaskMemAlloc(sizeof(LPOLESTR)*cPackProgId);
        if (!pszProgId) {
            hr = E_OUTOFMEMORY;
            ERROR_ON_FAILURE(hr);
        }
        
        for (count = 0, cPackProgId = 0; count < pPackageDetail->pActInfo->cClasses; count++) {
            // for each clsid
            DWORD cClassProgId, j = 0;
            for (cClassProgId = 0; cClassProgId < pPackageDetail->pActInfo->pClasses[count].cProgId;
                                                                               cClassProgId++) 
            {
                // for each progid within ClassDetail
                for (j = 0; j < cPackProgId; j++)
                {
                    if (_wcsicmp(pszProgId[j], 
                               pPackageDetail->pActInfo->pClasses[count].prgProgId[cClassProgId]) == 0)
                        break;
                }
                // needs to be added if there are no dups.
                if (j == cPackProgId)
                {
                    pszProgId[cPackProgId] =
                        pPackageDetail->pActInfo->pClasses[count].prgProgId[cClassProgId];
                    _wcslwr(pszProgId[cPackProgId]);
                    CSDBGPrint((L"AddPackage: Progid = %s\n", pszProgId[cPackProgId]));
                    cPackProgId++;
                }
            }
        }
        
        if (cPackProgId) {
            PackStrArrToAttr(pAttr+cAttr, PROGIDLIST, pszProgId, cPackProgId); cAttr++;
        }
        
        CoTaskMemFree(pszProgId);
        
        if (pPackageDetail->pActInfo->cShellFileExt) {
            //
            // Store a tuple in the format <file ext>:<priority>
            //
            pszFileExt = (LPOLESTR *)CoTaskMemAlloc((pPackageDetail->pActInfo->cShellFileExt) * sizeof(LPOLESTR));
            if (!pszFileExt)
            {
                hr = E_OUTOFMEMORY;
                ERROR_ON_FAILURE(hr);
            }
            for (count = 0; count < pPackageDetail->pActInfo->cShellFileExt; count++)
            {
                pszFileExt[count] = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) *
                    (wcslen(pPackageDetail->pActInfo->prgShellFileExt[count])+1+2+1));
                if (!pszFileExt[count]) 
                {
                    FREEARR(pszFileExt, count);
                    ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
                }
                // format is fileext+:+nn+NULL where nn = 2 digit priority
                wsprintf(pszFileExt[count], L"%s:%2d",
                    pPackageDetail->pActInfo->prgShellFileExt[count],
                    pPackageDetail->pActInfo->prgPriority[count]%100);

                _wcslwr(pszFileExt[count]);
            }
            PackStrArrToAttr(pAttr+cAttr, PKGFILEEXTNLIST, pszFileExt,
                pPackageDetail->pActInfo->cShellFileExt); cAttr++;
        }
        
        if (pPackageDetail->pActInfo->cInterfaces) {
            pszGuid2 = (LPOLESTR *)CoTaskMemAlloc((pPackageDetail->pActInfo->cInterfaces)*sizeof(LPOLESTR));
            if (!pszGuid2) {
                ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
            }
            
            for (count = 0; (count < (pPackageDetail->pActInfo->cInterfaces)); count++) {
                pszGuid2[count] = (LPOLESTR)CoTaskMemAlloc(STRINGGUIDLEN*sizeof(WCHAR));
                if (!pszGuid2[count]) {
                    FREEARR(pszGuid2, count);
                    ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
                }
                
                StringFromGUID((pPackageDetail->pActInfo->prgInterfaceId)[count], pszGuid2[count]);
            }
            
            PackStrArrToAttr(pAttr+cAttr, PKGIIDLIST, pszGuid2, pPackageDetail->pActInfo->cInterfaces);
            cAttr++;
        }
/*        
        
        if (pPackageDetail->pActInfo->cTypeLib) {
            pszGuid3 = (LPOLESTR *)CoTaskMemAlloc((pPackageDetail->pActInfo->cTypeLib)*sizeof(LPOLESTR));
            if (!pszGuid3) {
                ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
            }
            
            for (count = 0; (count < (pPackageDetail->pActInfo->cTypeLib)); count++) {
                pszGuid3[count] = (LPOLESTR)CoTaskMemAlloc(STRINGGUIDLEN*sizeof(WCHAR));
                if (!pszGuid3[count]) {
                    FREEARR(pszGuid3, count);
                    ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
                }
                
                StringFromGUID((pPackageDetail->pActInfo->prgTlbId)[count], pszGuid3[count]);
            }
            
            PackStrArrToAttr(pAttr+cAttr, PKGTLBIDLIST, pszGuid3, pPackageDetail->pActInfo->cTypeLib);
            cAttr++;
        }
*/
    }
   
    // fill in the platforminfo
    
    // BUGBUG::***os version
    if (pPackageDetail->pPlatformInfo->cPlatforms) {
        pdwArch = (DWORD *)CoTaskMemAlloc(sizeof(DWORD)*(pPackageDetail->pPlatformInfo->cPlatforms));
        if (!pdwArch)
            ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);

        for (count = 0; (count < (pPackageDetail->pPlatformInfo->cPlatforms)); count++)
            UnpackPlatform (pdwArch+count, (pPackageDetail->pPlatformInfo->prgPlatform)+count);
        
        PackDWArrToAttr(pAttr+cAttr, ARCHLIST, pdwArch, pPackageDetail->pPlatformInfo->cPlatforms);
        cAttr++;
    }
    
    if (pPackageDetail->pPlatformInfo->cLocales) {
        PackDWArrToAttr(pAttr+cAttr, LOCALEID, pPackageDetail->pPlatformInfo->prgLocale,
            pPackageDetail->pPlatformInfo->cLocales);
        cAttr++;
    }
    
    // fill in the installinfo
    
    PackStrToAttr(pAttr+cAttr, PACKAGENAME, pPackageDetail->pszPackageName);
    cAttr++;

    PackDWToAttr(pAttr+cAttr, PACKAGETYPE, (DWORD)(pPackageDetail->pInstallInfo->PathType));
    cAttr++;
    
    if (pPackageDetail->pInstallInfo->pszScriptPath) {
        PackStrToAttr(pAttr+cAttr, SCRIPTPATH, pPackageDetail->pInstallInfo->pszScriptPath);
        cAttr++;
    }
    
    if (pPackageDetail->pInstallInfo->pszSetupCommand) {
        PackStrToAttr(pAttr+cAttr, SETUPCOMMAND, pPackageDetail->pInstallInfo->pszSetupCommand);
        cAttr++;
    }
    
    if (pPackageDetail->pInstallInfo->pszUrl) {
        PackStrToAttr(pAttr+cAttr, HELPURL, pPackageDetail->pInstallInfo->pszUrl);
        cAttr++;
    }
    
    //
    // Store the current USN
    //
    GetCurrentUsn(szUsn);
    
    PackStrToAttr(pAttr+cAttr, PKGUSN, szUsn);
    cAttr++;
    
    // package flags
    PackDWToAttr(pAttr+cAttr, PACKAGEFLAGS, pPackageDetail->pInstallInfo->dwActFlags);
    cAttr++;
    
    // product code, different from pkg guid
    PackGUIDToAttr(pAttr+cAttr, PRODUCTCODE, &(pPackageDetail->pInstallInfo->ProductCode));
    cAttr++;
    
    // Mvipc
    PackGUIDToAttr(pAttr+cAttr, MVIPC, &(pPackageDetail->pInstallInfo->Mvipc));
    cAttr++;

    // Hi Version of the package
    PackDWToAttr(pAttr+cAttr, VERSIONHI, pPackageDetail->pInstallInfo->dwVersionHi);
    cAttr++;

    
    // Low Version of the package
    PackDWToAttr(pAttr+cAttr, VERSIONLO, pPackageDetail->pInstallInfo->dwVersionLo);
    cAttr++;
    
    // Revision
    PackDWToAttr(pAttr+cAttr, REVISION, pPackageDetail->pInstallInfo->dwRevision);
    cAttr++;

    // scriptsize
    PackDWToAttr(pAttr+cAttr, SCRIPTSIZE, pPackageDetail->pInstallInfo->cScriptLen);
    cAttr++;
    
    // uilevel
    PackDWToAttr (pAttr+cAttr, UILEVEL, (DWORD)pPackageDetail->pInstallInfo->InstallUiLevel);
    cAttr++;
    
    // adding cUpgrade number of Classstore/PackageGuid combinations
    if (pPackageDetail->pInstallInfo->cUpgrades) 
    {
        WCHAR szPackageGuid[_MAX_PATH];

        rpszUpgrades = (LPOLESTR *)CoTaskMemAlloc(sizeof(LPOLESTR)*pPackageDetail->pInstallInfo->cUpgrades);
        if (!rpszUpgrades)
            ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);

        for (count = 0; (count < pPackageDetail->pInstallInfo->cUpgrades); count++) 
        {
            UINT len = wcslen(pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].szClassStore);
            rpszUpgrades[count] = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR) *(36+PKG_UPG_DELIM1_LEN+len+PKG_UPG_DELIM2_LEN+2+2));
                                                        // Guid size+::+length++:+flagDigit+2 
            if (!rpszUpgrades[count])
            {
                FREEARR(rpszUpgrades, count);  
                ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
            }

            StringFromGUID(pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].PackageGuid, 
                            szPackageGuid);
            wsprintf(rpszUpgrades[count], L"%s%s%s%s%02x",
                        pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].szClassStore,
                        PKG_UPG_DELIMITER1,
                        szPackageGuid,
                        PKG_UPG_DELIMITER2, 
                        pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag%16);
        }
          
        PackStrArrToAttr(pAttr+cAttr, UPGRADESPACKAGES, rpszUpgrades,
            pPackageDetail->pInstallInfo->cUpgrades); 
        cAttr++;
    }
    
    // Fill in the source list 
    // Maintain the serial number associated with the sources. Order matters!!
    if (pPackageDetail->cSources) 
    {
        rpszSources = (LPOLESTR *)CoTaskMemAlloc(sizeof(LPOLESTR)*(pPackageDetail->cSources));
        if (!rpszSources)
            ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);

        for (count = 0; count < (pPackageDetail->cSources); count++) 
        {
            rpszSources[count] = (LPOLESTR)CoTaskMemAlloc(sizeof(WCHAR)*(wcslen(pPackageDetail->pszSourceList[count])+
                                                    1+1+NumDigits10(pPackageDetail->cSources)));
            if (!rpszSources[count])
            {
                FREEARR(rpszSources, count);
                ERROR_ON_FAILURE(hr=E_OUTOFMEMORY);
            }
            wsprintf(rpszSources[count], L"%d:%s", count, pPackageDetail->pszSourceList[count]);
        }
        
        PackStrArrToAttr(pAttr+cAttr, MSIFILELIST, rpszSources, pPackageDetail->cSources);
        cAttr++;
    }
    
    // fill in the categories
    // Add the package Categories
    if (pPackageDetail->cCategories)
    {
        pszGuid4 = (LPOLESTR *)CoTaskMemAlloc((pPackageDetail->cCategories) * sizeof(LPOLESTR));
        if (!pszGuid4)
        {
            ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
        }
        
        for (count = 0; (count < pPackageDetail->cCategories); count++)
        {
            pszGuid4[count] = (LPOLESTR)CoTaskMemAlloc(STRINGGUIDLEN*sizeof(WCHAR));
            if (!pszGuid4[count])
            {
                FREEARR(pszGuid4, count);
                ERROR_ON_FAILURE(hr = E_OUTOFMEMORY);
            }
            
            StringFromGUID((pPackageDetail->rpCategory)[count], pszGuid4[count]);
        }
        
        PackStrArrToAttr(pAttr+cAttr, PKGCATEGORYLIST, pszGuid4, pPackageDetail->cCategories);
        cAttr++;
    }
    
    // fill in the vendor
    
    // Publisher
    if (pPackageDetail->pszPublisher) 
    {
        PackStrToAttr(pAttr+cAttr, PUBLISHER, pPackageDetail->pszPublisher);
        cAttr++;
    }
    
    /*    
    //
    // Store the script in the directory
    //
    if ((pPackageDetail->pInstallInfo->dwActFlags & SCRIPT_IN_DIRECTORY) && 
    (pPackageDetail->pInstallInfo->cScriptLen))
    {
    
      if ((pPackageDetail->pInstallInfo->cScriptLen) &&
      (!IsValidReadPtrIn(pPackageDetail->pInstallInfo->pScript, pPackageDetail->pInstallInfo->cScriptLen)))
      return E_INVALIDARG;
      
        if (pPackageDetail->pInstallInfo->cScriptLen)
        {
        PackBinToAttr(pAttr+cAttr, PKGSCRIPT, pPackageDetail->pInstallInfo->pScript,
						  pPackageDetail->pInstallInfo->cScriptLen);
                          cAttr++;
                          }
                          }
    */
    
    hr = ADSICreateDSObject(m_ADsPackageContainer, szRDN, pAttr, cAttr);
    ERROR_ON_FAILURE(hr);

    memset(pPkgGuid, 0, sizeof(GUID));

//    memcpy(pPkgGuid, &PackageGuidId, sizeof(GUID));

    hr = GetPackageGuid(szRDN, pPkgGuid);
    ERROR_ON_FAILURE(hr);

    fPackageCreated = TRUE;

    if (pPackageDetail->pActInfo)
    {
        for (count = 0; count < pPackageDetail->pActInfo->cClasses; count++) {
            hr = NewClass((pPackageDetail->pActInfo->pClasses)+count);
            ERROR_ON_FAILURE(hr);
        }
    }
    
    if (!(pPackageDetail->pInstallInfo->dwActFlags & ACTFLG_Uninstall) &&
        !(pPackageDetail->pInstallInfo->dwActFlags & ACTFLG_Orphan))
    {
        if (pPackageDetail->pInstallInfo->cUpgrades)
        {
            //
            // Need to fixup the other packages that this package upgrades
            //
            for (count = 0; (count < pPackageDetail->pInstallInfo->cUpgrades); count++)
            {
                if ((pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag & UPGFLG_Uninstall) ||
                    (pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag & UPGFLG_NoUninstall))
                {
                    DWORD Flags = 0;

                    if (pPackageDetail->pInstallInfo->prgUpgradeInfoList[count].Flag & UPGFLG_Enforced)
                        Flags = UPGFLG_Enforced;

                }
            }
            for (count = 0; (count < pPackageDetail->pInstallInfo->cUpgrades); count++)
                CoTaskMemFree(rpszUpgrades[count]);
            CoTaskMemFree(rpszUpgrades);
        }
    }
    
    if (SUCCEEDED(hr))
    {
        //
        // Update Store Usn
        //
        UpdateStoreUsn(m_ADsContainer, szUsn);
    }
    
Error_Cleanup:
    for (count = 0; count < cAttr; count++)
        FreeAttr(pAttr[count]);
    
    if (pszGuid1) {
        for (count = 0; (count < pPackageDetail->pActInfo->cClasses); count++)
            CoTaskMemFree(pszGuid1[count]);
        CoTaskMemFree(pszGuid1);
    }
    
    if (pszGuid2) {
        for (count = 0; (count < (pPackageDetail->pActInfo->cInterfaces)); count++)
            CoTaskMemFree(pszGuid2[count]);
        CoTaskMemFree(pszGuid2);
    }
    
    if (pszGuid3) {
        for (count = 0; (count < (pPackageDetail->pActInfo->cTypeLib)); count++)
            CoTaskMemFree(pszGuid3[count]);
        CoTaskMemFree(pszGuid3);
    }
    
    if (pszGuid4) {
        for (count = 0; (count < pPackageDetail->cCategories); count++)
            CoTaskMemFree(pszGuid4[count]);
        CoTaskMemFree(pszGuid4);
    }
    
    if (pszFileExt) {
        for (count = 0; (count < pPackageDetail->pActInfo->cShellFileExt); count++)
            CoTaskMemFree(pszFileExt[count]);
        CoTaskMemFree(pszFileExt);
    }
    
    if (pdwArch) {
        CoTaskMemFree(pdwArch);
    }
    
    if (rpszSources) 
    {
        for (count = 0; (count < pPackageDetail->cSources); count++)
            CoTaskMemFree(rpszSources[count]);
        CoTaskMemFree(rpszSources);
    }

    //
    // On failure, the package should be removed from the ds if we
    // created it there.
    //
    if (FAILED(hr) && (fPackageCreated))
    {
        HRESULT hrDeleted;
        LPWSTR  wszPackageFullPath;

        //
        // Need to get a full path to the package in order to delete it.
        //
        hrDeleted = BuildADsPathFromParent(m_szPackageName, szRDN, &wszPackageFullPath);

        ASSERT(SUCCEEDED(hrDeleted));

        CSDBGPrint((L"AddPackage failed, attempting to remove deleted package with path %s\n", wszPackageFullPath));

        hrDeleted = DeletePackage(wszPackageFullPath);

        CSDBGPrint((L"DeletePackage returned %x\n", hrDeleted));
        
        //
        // Free the full path
        //
        CoTaskMemFree(wszPackageFullPath);
    }

    return RemapErrorCode(hr, m_szContainerName);
}

//+
//
// Cleanup old packages from Class Store based on lastChangeTime
//

HRESULT CClassContainer::Cleanup (
                                  FILETIME        *pTimeBefore
                                  )
{
    //
    // Delete all packages marked as "Uninstall"
    // OR "Orphan" and are older than the time given
    //
    
    ULONG               cRowsFetched = 0;
    ADS_SEARCH_HANDLE   hADsSearchHandle = NULL;
    WCHAR               szFilter[_MAX_PATH], szRDN[_MAX_PATH];
    HRESULT             hr = S_OK;
    ADS_ATTR_INFO       pAttr;
    SYSTEMTIME          SystemTime;
    ADS_SEARCH_COLUMN   column;
    DWORD               dwPackageFlags;
    LPOLESTR            pszPackageId = NULL;
    
    if ((!pTimeBefore) ||
        !IsValidReadPtrIn(pTimeBefore, sizeof(FILETIME)))
        return E_INVALIDARG;
    
    FileTimeToSystemTime(
        (CONST FILETIME *) pTimeBefore, 
        &SystemTime);  
    
    wsprintf (szFilter, 
        L"(%s<=%04d%02d%02d%02d%02d%02d)", 
        PKGUSN,
        SystemTime.wYear,
        SystemTime.wMonth,
        SystemTime.wDay,
        SystemTime.wHour,
        SystemTime.wMinute,
        SystemTime.wSecond);
    
    // execute the search and keep the handle returned.
    hr = ADSIExecuteSearch(m_ADsPackageContainer, szFilter, pszDeleteAttrNames,
        cDeleteAttr, &hADsSearchHandle);
    
    hr = ADSIGetFirstRow(m_ADsPackageContainer, hADsSearchHandle);
    
    while (TRUE)
    {
        if ((FAILED(hr)) || (hr == S_ADS_NOMORE_ROWS))
            break;
        
        dwPackageFlags = 0;
        
        // Get the Package State
        hr = ADSIGetColumn(m_ADsPackageContainer, hADsSearchHandle, PACKAGEFLAGS, &column);
        
        if (SUCCEEDED(hr))
        {
            UnpackDWFrom(column, &dwPackageFlags);
            ADSIFreeColumn(m_ADsPackageContainer, &column);
        }
        
        //
        // Check flag values to see if this package is Orphaned or Uninstalled
        //
        
        if ((dwPackageFlags & ACTFLG_Orphan) || (dwPackageFlags & ACTFLG_Uninstall)) 
        {
            
            hr = ADSIGetColumn(m_ADsPackageContainer, hADsSearchHandle, OBJECTDN, &column);
            
            if (SUCCEEDED(hr))
            {
                WCHAR    * szDN = NULL;

                UnpackStrFrom(column, &szDN);
                hr = DeletePackage(szDN);

                ADSIFreeColumn(m_ADsPackageContainer, &column);
                ERROR_ON_FAILURE(hr);
            }
        }
        hr = ADSIGetNextRow(m_ADsPackageContainer, hADsSearchHandle);  
    }
    
Error_Cleanup:
    if (hADsSearchHandle)
        ADSICloseSearchHandle(m_ADsPackageContainer, hADsSearchHandle);
    return RemapErrorCode(hr, m_szContainerName);
}