/*===================================================================
Microsoft IIS

Microsoft Confidential.
Copyright 1997 Microsoft Corporation. All Rights Reserved.

Component: WAMREG

File: mtsconfig.cpp

    implementation of supporting functions for WAMREG, including

    interface to Add/Remove Component from a MTS package,

History: LeiJin created on 9/24/1997

Note:

===================================================================*/
#include "common.h"
#include "auxfunc.h"
#include "dbgutil.h"
#include "export.h"


/*===================================================================
  Define the global variables and types
======================================================================*/

//
// Following is a list of all the WAMREG/MTS properties for Package creation
//  Format:
//     (prop-symbolic-name, property-name-string)
//
//  WAMREG_MTS_PROPERTY()  -> means property for NT & Win9x
//  WAMREG_MTS_NTPROPERTY()  -> means property for NT only
//

# define ALL_WAMREG_MTS_PROPERTY()   \
  WAMREG_MTS_PROPERTY( WM_ID,          L"ID") \
  WAMREG_MTS_PROPERTY( WM_NAME,        L"Name") \
  WAMREG_MTS_PROPERTY( WM_CREATED_BY,  L"CreatedBy") \
  WAMREG_MTS_PROPERTY( WM_RUN_FOREVER, L"RunForever") \
  WAMREG_MTS_NTPROPERTY( WM_IDENTITY,  L"Identity") \
  WAMREG_MTS_NTPROPERTY( WM_PASSWORD,  L"Password") \
  WAMREG_MTS_PROPERTY( WM_ACTIVATION,  L"Activation") \
  WAMREG_MTS_PROPERTY( WM_CHANGEABLE,  L"Changeable") \
  WAMREG_MTS_PROPERTY( WM_DELETABLE,   L"Deleteable") \
  WAMREG_MTS_PROPERTY( WM_SECSUPP,     L"AccessChecksLevel") \
  WAMREG_MTS_PROPERTY( WM_APPLICATIONACCESSCHECKS,L"ApplicationAccessChecksEnabled") \


//
// Let us expand the macros here for defining the symbolic-name
//
//
# define WAMREG_MTS_PROPERTY( symName, pwsz)   symName, 
# define WAMREG_MTS_NTPROPERTY( symName, pwsz)   symName, 

enum WAMREG_MTS_PROP_NAMES {
  ALL_WAMREG_MTS_PROPERTY()  
  MAX_WAMREG_MTS_PROP_NAMES         // sentinel element
};

# undef WAMREG_MTS_PROPERTY
# undef WAMREG_MTS_NTPROPERTY


struct MtsProperty {
    LPCWSTR m_pszPropName;
    BOOL    m_fWinNTOnly;
};

//
// Let us expand the macros here for defining the property strings
//
//
# define WAMREG_MTS_PROPERTY( symName, pwsz)   { pwsz, FALSE },
# define WAMREG_MTS_NTPROPERTY( symName, pwsz)   { pwsz, TRUE },

static const MtsProperty g_rgWamRegMtsProperties[]= {
    ALL_WAMREG_MTS_PROPERTY()  
    { NULL, FALSE}           // sentinel element
};

# define NUM_WAMREG_MTS_PROPERTIES  \
   ((sizeof(g_rgWamRegMtsProperties)/sizeof(g_rgWamRegMtsProperties[0])) - 1)

# undef WAMREG_MTS_PROPERTY
# undef WAMREG_MTS_NTPROPERTY


#define ReleaseInterface(p) if (p) { p->Release(); p = NULL; }


/*===================================================================
WamRegPackageConfig    

Constructor.

Parameter:
NONE;
===================================================================*/
WamRegPackageConfig::WamRegPackageConfig()
:     m_pCatalog(NULL),
    m_pPkgCollection(NULL),
    m_pCompCollection(NULL),
    m_pPackage(NULL)
{

}

/*===================================================================
~WamRegPackageConfig    

Destructor. 
By the time the object gets destructed, all resources should be freed.
We do most of the cleanup inside WamReqPackageConfig::Cleanup() so
 that callers call that function separately to cleanup state
 especially if the caller also calls CoUninitialize().
WamRegPackageConfig should be cleaned up before CoUninitialize()

Parameter:
NONE;
===================================================================*/
WamRegPackageConfig::~WamRegPackageConfig()
{
    Cleanup();

    // insane checks to ensure everything is happy here
    DBG_ASSERT(m_pCatalog == NULL);
    DBG_ASSERT(m_pPkgCollection == NULL);
    DBG_ASSERT(m_pCompCollection == NULL);
    DBG_ASSERT(m_pPackage == NULL);
}

VOID
WamRegPackageConfig::Cleanup(VOID)
{
    if (m_pPackage != NULL ) {
        RELEASE( m_pPackage);
        m_pPackage = NULL;
    }
    
    if (m_pCompCollection != NULL) {
        RELEASE (m_pCompCollection);
        m_pCompCollection = NULL;
    }

    if (m_pPkgCollection != NULL ) {
        RELEASE(m_pPkgCollection);
        m_pPkgCollection = NULL;
    }

    if (m_pCatalog != NULL ) {
        RELEASE(m_pCatalog);
        m_pCatalog = NULL;
    }

} // WamPackageConfig::Cleanup()


/*===================================================================
ReleaseAll

Release all resources.

Parameter:
NONE;
===================================================================*/
VOID WamRegPackageConfig::ReleaseAll
(
)
{
    RELEASE(m_pPackage);
    RELEASE(m_pCompCollection);

    //
    // NOTE: I am not releasing m_pCatalog, m_pPkgCollection
    //  These will be released by the Cleanup().
    //
}

/*===================================================================
CreateCatalog

CoCreateObject of an MTS Catalog object if the Catalog object has not been
created.

Parameter:
NONE;
===================================================================*/
HRESULT WamRegPackageConfig::CreateCatalog
(
VOID
)
{
    HRESULT hr = NOERROR;

    DBG_ASSERT(m_pCatalog == NULL);
    DBG_ASSERT(m_pPkgCollection == NULL);

    // Create instance of the catalog object
    hr = CoCreateInstance(CLSID_COMAdminCatalog
                    , NULL
                    , CLSCTX_SERVER
                    , IID_ICOMAdminCatalog
                    , (void**)&m_pCatalog);

    if (FAILED(hr)) {
        DBGPRINTF((DBG_CONTEXT, 
                   "Failed to CoCreateInstance of Catalog Object.,hr = %08x\n",
                   hr));
    }
    else {
        DBG_ASSERT(m_pCatalog != NULL);

        BSTR  bstr;
        
        //
        // Get the Packages collection
        //
        bstr = SysAllocString(L"Applications");
        hr = m_pCatalog->GetCollection(bstr, (IDispatch**)&m_pPkgCollection);
        FREEBSTR(bstr);
        if (FAILED(hr)) {

            // Release the Catalog in case we are called again
            RELEASE(m_pCatalog);

            DBGPRINTF((DBG_CONTEXT, 
                       "m_pCatalog(%08x)->GetCollection() failed, hr = %08x\n",
                       m_pCatalog,
                       hr));
        } else {
            DBG_ASSERT( m_pPkgCollection != NULL);
        }
            
    }

    return hr;
} // WamRegPackageConfig::CreateCatalog()



/*===================================================================
SetCatalogObjectProperty    

Get a SafeArray contains one ComponentCLSID

Parameter:
szComponentCLSID    the CLSID need to be put in the safe array
paCLSIDs            pointer to a pointer of safe array(safe array provided by caller).

Return:        HRESULT
Side Affect:

Note:
===================================================================*/
HRESULT WamRegPackageConfig::GetSafeArrayOfCLSIDs
(
IN LPCWSTR    szComponentCLSID,
OUT SAFEARRAY**    paCLSIDs
)
{
    SAFEARRAY*          aCLSIDs = NULL;
    SAFEARRAYBOUND      rgsaBound[1];
    LONG                Indices[1];
    VARIANT                varT;
    HRESULT             hr = NOERROR;

    DBG_ASSERT(szComponentCLSID && paCLSIDs);
    DBG_ASSERT(*paCLSIDs == NULL);
    
    // PopulateByKey is expecting a SAFEARRAY parameter input,
    // Create a one element SAFEARRAY, the one element of the SAFEARRAY contains
    // the packageID.
    rgsaBound[0].cElements = 1;
    rgsaBound[0].lLbound = 0;
    aCLSIDs = SafeArrayCreate(VT_VARIANT, 1, rgsaBound);

    if (aCLSIDs)
        {
        Indices[0] = 0;

        VariantInit(&varT);
        varT.vt = VT_BSTR;
        varT.bstrVal = SysAllocString(szComponentCLSID);
        hr = SafeArrayPutElement(aCLSIDs, Indices, &varT);
        VariantClear(&varT);

        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to call SafeArrayPutElement, CLSID is %S, hr %08x\n",
                szComponentCLSID,
                hr));
       
            if (aCLSIDs != NULL)
                {
                HRESULT hrT = SafeArrayDestroy(aCLSIDs);
                if (FAILED(hrT))
                    {
                    DBGPRINTF((DBG_CONTEXT, "Failed to call SafeArrayDestroy(aCLSIDs), hr = %08x\n",
                        hr));
                    }
                aCLSIDs = NULL;
                }
            }
        }
    else
        {
        hr = HRESULT_FROM_WIN32(GetLastError());
        DBGPRINTF((DBG_CONTEXT, "Failed to call SafeArrayCreate, hr %08x\n",
                hr));
        }

    *paCLSIDs = aCLSIDs;
    return hr;
}



/*===================================================================
SetComponentObjectProperty    

Set component level property.

Parameter:
pComponent     - pointer to the ICatalogObject(MTS) used to update property
szPropertyName - Name of the property
szPropertyValue- Value of the property
fPropertyValue - If szPropertyValue is NULL, use fPropertyValue

Return:        HRESULT
Side Affect:

Note:
===================================================================*/
HRESULT    WamRegPackageConfig::SetComponentObjectProperty
(
IN ICatalogObject * pComponent,
IN LPCWSTR          szPropertyName,
IN LPCWSTR          szPropertyValue,
BOOL                fPropertyValue
)
{
    BSTR    bstr    = NULL;
    HRESULT hr      = NOERROR;
    VARIANT    varT;
    
    VariantInit(&varT);
    bstr = SysAllocString(szPropertyName);

    if (szPropertyValue != NULL)
        {
        varT.vt = VT_BSTR;
        varT.bstrVal = SysAllocString(szPropertyValue);
        }
    else
        {
        //
        // COM+ regcongize -1 as TRUE, and 0 as FALSE.  I believe the root is from VB.
        //
        varT.vt = VT_BOOL;
        varT.boolVal = (fPropertyValue) ? VARIANT_TRUE : VARIANT_FALSE;
        }
        
    hr = pComponent->put_Value(bstr, varT);
        
    FREEBSTR(bstr);
    VariantClear(&varT);

    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT,
                   "MTS-Component(%08x)::SetProperty(%S => %S) failed;"
                   " hr %08x\n",
                   pComponent, szPropertyName, szPropertyValue, hr));
        }        
    return hr;
}


/*===================================================================
WamRegPackageConfig::SetComponentObjectProperties()

Sets the componnet properties for newly created component that houses
the WAM unit

Parameter:
szComponentCLSID  -  CLSID for the component that is newly created

Return:        HRESULT

Side Affect:
  If there is a failure all the previously set values are not cleared.
  The caller should make sure that the proper cleanup of package happens
  on partial errors.

Note:
===================================================================*/
HRESULT
WamRegPackageConfig::SetComponentObjectProperties(
   IN LPCWSTR    szComponentCLSID
)
    {
    HRESULT         hr;
    SAFEARRAY*      aCLSIDs = NULL;
    long            lCompCount = 0;
    ICatalogObject* pComponent = NULL;
    BOOL            fFound;

    DBG_ASSERT( m_pCompCollection != NULL);

    //
    // Create the array containing the CLSIDs from the component name
    //  this will be used to find our object in MTS and set properties
    //   on the same
    //
    
    hr = GetSafeArrayOfCLSIDs(szComponentCLSID, &aCLSIDs);
    if (FAILED(hr)) 
        {
    
        DBGPRINTF((DBG_CONTEXT, 
                   "Failed in GetSafeArrayOfCLSIDs(%S). hr=%08x\n",
                   szComponentCLSID, hr));
        goto LErrExit;
        }
    
    hr = m_pCompCollection->PopulateByKey(aCLSIDs);
    if (FAILED(hr)) 
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call PopulateByKey(), hr = %08x\n",
                   hr));
        goto LErrExit;
        }

    // Find our component in the list (should be the only one)
    hr = m_pCompCollection->get_Count(&lCompCount);
    if (FAILED(hr)) 
        {
        DBGPRINTF((DBG_CONTEXT,
                   "Failed in CompCollection(%08x)::get_Count(). hr = %08x\n",
                   m_pCompCollection, hr));
        goto LErrExit;
        }

       
    //
    // Load the component object so that we can set properties
    //
    fFound = FALSE;
    if (SUCCEEDED(hr) && lCompCount == 1) 
        {
        hr = m_pCompCollection->get_Item(0, (IDispatch**)&pComponent);
        
        if (FAILED(hr)) 
            {
            
            DBGPRINTF((DBG_CONTEXT,
                       "Failed in CompCollection(%08x)::get component() hr=%08x\n",
                       m_pCompCollection, hr));
            goto LErrExit;
            } 
        else 
            {

            // Found it
            DBG_ASSERT(pComponent);
            fFound = TRUE;
            }
        }
        
    if (fFound) 
        {

        //
        // Component Properties       InProc            OutOfProc
        // ---------------------     --------           ----------
        // Synchronization              0               same
	    // Transaction              "Not Supported"     same
	    // JustInTimeActivation         N               same
	    // IISIntrinsics                N               same
	    // COMTIIntrinsics              N               same
	    // ComponentAccessChecksEnabled     0               same
	    // MustRunInDefaultContext      TRUE            same
	    //

        hr = SetComponentObjectProperty( pComponent, L"Synchronization", L"0");
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }
        
        hr = SetComponentObjectProperty( pComponent, L"ComponentAccessChecksEnabled", L"0");
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }
                
        hr = SetComponentObjectProperty( pComponent, L"Transaction", L"0");
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }
        
        hr = SetComponentObjectProperty( pComponent, L"JustInTimeActivation",NULL,FALSE);
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }

        hr = SetComponentObjectProperty( pComponent, L"IISIntrinsics", NULL, FALSE);
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }

        hr = SetComponentObjectProperty( pComponent, L"COMTIIntrinsics", NULL, FALSE);
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }
        
        hr = SetComponentObjectProperty(pComponent, L"EventTrackingEnabled", L"N");
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }
        
        hr = SetComponentObjectProperty(pComponent, L"MustRunInDefaultContext", NULL, TRUE);
        if (FAILED(hr)) 
            {
            goto LErrExit;
            }
        } 
    else 
        {

        DBGPRINTF((DBG_CONTEXT, 
                   "Unable to find newly created WAM component in package\n"));
        DBG_ASSERT(FALSE);
        }

LErrExit:    
    RELEASE(pComponent);
    
    if (aCLSIDs != NULL) {

        HRESULT hrT = SafeArrayDestroy(aCLSIDs);
        if (FAILED(hrT)) {
            
            DBGPRINTF((DBG_CONTEXT, 
                       "Failed to call SafeArrayDestroy(aCLSIDs=%08x),"
                       " hr = %08x\n",
                       aCLSIDs, hr));
        }
        aCLSIDs = NULL;
    }
    
    return ( hr);
} //  // WamRegPackageConfig::SetComponentObjectProperties()



/*===================================================================
SetPackageObjectProperty    

Set package level property.

Parameter:
szPropertyName  Name of the property
szPropertyValue Value of the property

Return:        HRESULT
Side Affect:

Note:
===================================================================*/
HRESULT    WamRegPackageConfig::SetPackageObjectProperty
(
IN LPCWSTR        szPropertyName,
IN LPCWSTR        szPropertyValue
)
{
    BSTR    bstr    = NULL;
    HRESULT hr      = NOERROR;
    VARIANT    varT;

    
    VariantInit(&varT);
    bstr = SysAllocString(szPropertyName);
    varT.vt = VT_BSTR;
    varT.bstrVal = SysAllocString(szPropertyValue);
    DBG_ASSERT(m_pPackage != NULL);
    hr = m_pPackage->put_Value(bstr, varT);
        
    FREEBSTR(bstr);
    VariantClear(&varT);

    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT,
                   "Set Catalog Object Property failed, "
                   "Component is %S, hr %08x\n",
                   szPropertyName,
                   hr));
        }        
    return hr;
} // WamRegPackageConfig::SetPackageObjectProperty()




/*===================================================================
WamRegPackageConfig::SetPackageProperties()

Sets package properties for all WAMREG properties.

Parameter:
rgpszValues:   An array containing pointers to string values to be used
               for setting up the WAMREG related properites for MTS catalog.

Return:        HRESULT
Side Affect:
  If there is a failure all the previously set values are not cleared.
  The caller should make sure that the proper cleanup of package happens
  on partial errors.

Note:
===================================================================*/
HRESULT    WamRegPackageConfig::SetPackageProperties
(
IN LPCWSTR    * rgpszValues
)
{
    HRESULT hr = NOERROR;

    DBG_ASSERT( m_pPackage);

    //
    // Loop through all properties and set the values for these 
    //  properties using the passed in array of strings.
    // UGLY: MTS likes to have string properties which need to be 
    //   fed in as BSTRs => very inefficient.
    //

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

        if ( (rgpszValues[i] == NULL)
             ) {
            
            //
            // This parameter is required only for certain cases.
            // Skip this parameter.
            //

            continue;
        }

        DBG_ASSERT( rgpszValues[i] != NULL);

        IF_DEBUG( WAMREG_MTS) {
            DBGPRINTF(( DBG_CONTEXT, 
                        "In Package(%08x) setting property %S to value %S\n",
                        m_pPackage, 
                        g_rgWamRegMtsProperties[i].m_pszPropName,
                        rgpszValues[i]
                        ));
        }

        //
        // Now let us set up the property in the MTS package
        //

        hr = SetPackageObjectProperty(g_rgWamRegMtsProperties[i].m_pszPropName,
                                      rgpszValues[i]);
        if ( FAILED (hr)) {
            DBGPRINTF((DBG_CONTEXT, "Failed to set property %S, value is %S\n",
                g_rgWamRegMtsProperties[i].m_pszPropName,
                rgpszValues[i]));
            break;
        }
    } // for all properties

    return (hr);
} // WamRegPackageConfig::SetPackageProperties()


BOOL WamRegPackageConfig::IsPackageInstalled
(
IN LPCWSTR szPackageID,
IN LPCWSTR szComponentCLSID
)
/*++
Routine Description:

    Determine if the WAM package is installed and is valid. Currently this
    is only called by setup.

Parameters

    IN LPCWSTR szPackageID          - Package ID
    IN LPCWSTR szComponentCLSID     - Component CLSID

Return Value

    BOOL    - True if package contains the component. False otherwise.

--*/
{
    HRESULT     hr;
    SAFEARRAY*  aCLSIDs = NULL;
    SAFEARRAY*  aCLSIDsComponent = NULL;

    DBG_ASSERT( m_pCatalog != NULL);
    DBG_ASSERT( m_pPkgCollection != NULL);

    long                    lPkgCount;
    BOOL                    fFound = FALSE;
    ICatalogCollection*     pCompCollection = NULL;
    
    // Only use the trace macro here, even for error conditions.
    // This routine may fail in a variety of ways, but we expect
    // to be able to fix any of them, only report an error if 
    // the failure is likely to impair the functionality of the
    // server.

    SETUP_TRACE(( 
        DBG_CONTEXT, 
        "CALL - IsPackageInstalled, Package(%S) Component(%S)\n",
        szPackageID,
        szComponentCLSID
        ));

    //
    // Get the package
    //

    hr = GetSafeArrayOfCLSIDs(szPackageID, &aCLSIDs);
    if (FAILED(hr))
        {
        SETUP_TRACE((
            DBG_CONTEXT, 
            "Failed to GetSafeArrayOfCLSIDs for %S, hr = %08x\n",
            szPackageID,
            hr
            ));
        goto LErrExit;
        }

    hr = m_pPkgCollection->PopulateByKey(aCLSIDs);
    if (FAILED(hr))
        {
        SETUP_TRACE((
            DBG_CONTEXT, 
            "Failed in m_pPkgCollection(%p)->PopulateByKey(), hr = %08x\n",
            m_pPkgCollection,
            hr
            ));
        goto LErrExit;
        }
    
    hr = m_pPkgCollection->get_Count(&lPkgCount);
    if (SUCCEEDED(hr) && lPkgCount == 1)
        {
        // 
        // We found the package. Now verify that it contains our component.
        //
        SETUP_TRACE((
            DBG_CONTEXT, 
            "Successfully retrieved package (%S).\n",
            szPackageID
            ));

        VARIANT varKey;
        BSTR    bstrComponentCollection;

        VariantInit(&varKey);
        varKey.vt = VT_BSTR;
        varKey.bstrVal = SysAllocString(szPackageID);

        // Get the "ComponentsInPackage" collection.
        bstrComponentCollection = SysAllocString(L"Components");
        hr = m_pPkgCollection->GetCollection(
                    bstrComponentCollection, 
                    varKey, 
                    (IDispatch**)&pCompCollection
                    );
        
        FREEBSTR(bstrComponentCollection);
        VariantClear(&varKey);
        if (FAILED(hr))
            {
            SETUP_TRACE((
                DBG_CONTEXT, 
                "Failed in m_pPkgCollection(%p)->GetCollection(), hr = %08x\n",
                m_pPkgCollection,
                hr
                ));
            goto LErrExit;
            }

        hr = GetSafeArrayOfCLSIDs(szComponentCLSID, &aCLSIDsComponent);
        if (FAILED(hr))
            {
            SETUP_TRACE((
                DBG_CONTEXT, 
                "Failed to GetSafeArrayOfCLSIDs for %S, hr = %08x\n",
                szComponentCLSID,
                hr
                ));
            goto LErrExit;
            }

        hr = pCompCollection->PopulateByKey( aCLSIDsComponent );
        if( FAILED(hr) )
            {
            SETUP_TRACE((
                DBG_CONTEXT, 
                "Failed in pCompCollection(%p)->PopulateByKey, hr = %08x\n",
                pCompCollection,
                hr
                ));
            goto LErrExit;
            }

        hr = pCompCollection->get_Count( &lPkgCount );
        if( SUCCEEDED(hr) && lPkgCount == 1 )
            {
            // Success! We found the package and it contains the 
            // correct component.

            SETUP_TRACE((
                DBG_CONTEXT, 
                "Successfully retrieved component (%S) from package (%S).\n",
                szComponentCLSID,
                szPackageID
                ));

            fFound = TRUE;
            }
        }

LErrExit:
    if (aCLSIDs != NULL)
        {
        SafeArrayDestroy(aCLSIDs);        
        aCLSIDs = NULL;
        }

    if( aCLSIDsComponent != NULL )
        {
        SafeArrayDestroy(aCLSIDsComponent);
        aCLSIDsComponent = NULL;
        }
    
    RELEASE( pCompCollection );  

    SETUP_TRACE(( 
        DBG_CONTEXT, 
        "RETURN - IsPackageInstalled, hr=%08x\n", 
        hr 
        ));
    return fFound;
}
/*===================================================================
RemovePackage    

Remove a Viper Package.

Parameter:
    szPackageID:    an MTS package ID.

Return:        HRESULT
Side Affect:

Note:
Remove an IIS package from MTS. So far, only been called from RemoveIISPackage.
RemoveComponentFromPackage() also removes a IIS package sometimes. 
Refer to that function header for info.
===================================================================*/
HRESULT WamRegPackageConfig::RemovePackage
(
IN LPCWSTR    szPackageID
)
{
    HRESULT                hr = NOERROR;
    long                lPkgCount = 0;
    long                lChanges;
    SAFEARRAY*          aCLSIDs = NULL;
    
    DBG_ASSERT(szPackageID);
    
    DBG_ASSERT( m_pCatalog != NULL);
    DBG_ASSERT( m_pPkgCollection != NULL);

    hr = GetSafeArrayOfCLSIDs(szPackageID, &aCLSIDs);        
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to get SafeArrayofCLSIDs, szPackageID is %S, hr %08x",
            szPackageID,
            hr));
        goto LErrExit;
        }
        
    //
    // Populate it
    //    
    hr = m_pPkgCollection->PopulateByKey(aCLSIDs);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call PopulateByKey(), hr = %08x\n",
            hr));
        goto LErrExit;
        }
        
    hr = m_pPkgCollection->get_Count(&lPkgCount);
    if (FAILED(hr))
        {
        IF_DEBUG(ERROR)
                {
                DBGPRINTF((DBG_CONTEXT, "pPkgCollection->Populate() failed, hr = %08x\n",
                    hr));
                }
        goto LErrExit;
        }
        
    if (SUCCEEDED(hr) && lPkgCount == 1)
        {
        hr = m_pPkgCollection->get_Item(0, (IDispatch**)&m_pPackage);
        if (FAILED(hr))
            {
            goto LErrExit;
            }
            
        // Found it - remove it and call Save Changes
        // First, Set Deleteable = Y property on package
        hr = SetPackageObjectProperty(L"Deleteable", L"Y");
        if (FAILED(hr))
            {
            goto LErrExit;
            }

        RELEASE(m_pPackage);
        
        // Let save the Deletable settings
        hr = m_pPkgCollection->SaveChanges(&lChanges);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Save the Deletable settings failed, hr = %08x\n",
                hr));
            goto LErrExit;
            }
            
        // Now we can delete
        hr = m_pPkgCollection->Remove(0);
        if (FAILED(hr))
            {                
            DBGPRINTF((DBG_CONTEXT, "Remove the Component from package failed, hr = %08x\n",
                hr));
            goto LErrExit;
            }

        // Aha, we should be able to delete now.
        hr = m_pPkgCollection->SaveChanges(&lChanges);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Save changes failed, hr = %08x\n",
                hr));
            goto LErrExit;
            }
        }

LErrExit:
    if (aCLSIDs != NULL)
        {
        HRESULT hrT = SafeArrayDestroy(aCLSIDs);
        if (FAILED(hrT))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to call SafeArrayDestroy(aCLSIDs), hr = %08x\n",
                hr));
            }
        aCLSIDs = NULL;
        }
        
    ReleaseAll();
        
    return hr;
}

/*===================================================================
CreatePackage    

Create a viper package.

Parameter:
szPackageID:            [in] Viper Package ID.
szPackageName:            [in] the name of the package.
szIdentity:                [in] Pakcage identity
szIdPassword:           [in] Package idneitty password


Return:        HRESULT
Side Affect:
NONE.

===================================================================*/
HRESULT WamRegPackageConfig::CreatePackage
(    
IN LPCWSTR    szPackageID,
IN LPCWSTR    szPackageName,
IN LPCWSTR    szIdentity,
IN LPCWSTR    szIdPassword
)
    {
    
    HRESULT     hr;
    SAFEARRAY*  aCLSIDs = NULL;

    DBG_ASSERT( m_pCatalog != NULL);
    DBG_ASSERT( m_pPkgCollection != NULL);

    long lPkgCount;
    BOOL fFound = FALSE;

    SETUP_TRACE(( 
        DBG_CONTEXT, 
        "CALL - CreatePackage ID(%S) Name(%S)\n",
        szPackageID,
        szPackageName
        ));
    
    //
    // Try to get the package.
    //
    SETUP_TRACE((
        DBG_CONTEXT, 
        "Checking to see if package ID(%S) Name(%S) exists.\n",
        szPackageID,
        szPackageName
        ));

    hr = GetSafeArrayOfCLSIDs(szPackageID, &aCLSIDs);
    if (FAILED(hr))
        {
        SETUP_TRACE_ERROR((
            DBG_CONTEXT, 
            "Failed to GetSafeArrayOfCLSIDs for %S, hr = %08x\n",
            szPackageID,
            hr
            ));
        goto LErrExit;
        }

    hr = m_pPkgCollection->PopulateByKey(aCLSIDs);
    if (FAILED(hr))
        {
        SETUP_TRACE_ERROR((
            DBG_CONTEXT, 
            "Failed in m_pPkgCollection(%p)->PopulateByKey(), hr = %08x\n",
            m_pPkgCollection,
            hr
            ));
        goto LErrExit;
        }
    
    hr = m_pPkgCollection->get_Count(&lPkgCount);
    if (SUCCEEDED(hr) && lPkgCount == 1)
        {
        //
        // Found the CLSID in PopulateByKey().
        //
        hr = m_pPkgCollection->get_Item(0, (IDispatch**)&m_pPackage);
        if (FAILED(hr))
            {
            SETUP_TRACE_ERROR((
                DBG_CONTEXT, 
                "Failed in m_pPkgCollection(%p)->get_Item(). Err=%08x\n",
                m_pPkgCollection, 
                hr
                ));
            goto LErrExit;
            }
        else
            {
            SETUP_TRACE(( 
                DBG_CONTEXT, 
                "CreatePackage - Package already exists, ID(%S), Name(%S)\n",
                szPackageID,
                szPackageName
                ));
            DBG_ASSERT(m_pPackage);
            fFound = TRUE;
            }
        }

    if ( SUCCEEDED(hr) )
    {

        if( !fFound )
        {
            SETUP_TRACE(( 
                DBG_CONTEXT, 
                "Package ID(%S) Name(%S) does not exist. Attempting to create it.\n",
                szPackageID,
                szPackageName
                ));
            //
            // The package does not already exist, we need to call Add() to 
            // add this package and then set it's properties.
            //
            hr = m_pPkgCollection->Add((IDispatch**)&m_pPackage);
            if ( FAILED(hr)) 
                {
                SETUP_TRACE_ERROR(( 
                    DBG_CONTEXT, 
                    "Failed in m_pPkgCollection(%p)->Add(). Err=%08x\n",
                    m_pPkgCollection, 
                    hr
                    ));
                goto LErrExit;
                }
        }
        
        DBG_ASSERT( SUCCEEDED( hr));
        DBG_ASSERT( m_pPackage != NULL);

        if( SUCCEEDED(hr) && m_pPackage != NULL )
        {
            //
            // Set the Package properties 
            //  first by initializing the array of values and then
            //  calling SetPackageProperties()
            //
        
            LPCWSTR rgpszValues[ MAX_WAMREG_MTS_PROP_NAMES];

            ZeroMemory( rgpszValues, sizeof( rgpszValues));

            if( fFound )
            {
                // For an existing package, we don't want to set the ID
                rgpszValues[ WM_ID]         = NULL;
            }
            else
            {
                rgpszValues[ WM_ID]         = szPackageID;
            }

            rgpszValues[ WM_NAME]       = szPackageName;
            rgpszValues[ WM_CREATED_BY] = 
                L"Microsoft Internet Information Services";

            rgpszValues[ WM_RUN_FOREVER] = L"Y";

            rgpszValues[ WM_IDENTITY]   = szIdentity;
            rgpszValues[ WM_PASSWORD]   = szIdPassword;
            rgpszValues[ WM_ACTIVATION] = L"Local";
            rgpszValues[ WM_CHANGEABLE] = L"Y";
            rgpszValues[ WM_DELETABLE]  = L"N";
            rgpszValues[ WM_SECSUPP] = L"0";
            rgpszValues[ WM_APPLICATIONACCESSCHECKS ] = L"N";
        
            //
            // Now that we have the properties setup, let us
            //  now set the properties in the MTS using catalog
            //  object
            //
            hr = SetPackageProperties( rgpszValues);
            if ( FAILED( hr)) 
            {
                SETUP_TRACE_ERROR(( 
                    DBG_CONTEXT, 
                    "Failed to set properties for package %p. Err=%08x\n",
                    m_pPackage, 
                    hr
                    ));
                goto LErrExit;
            }

            long lChanges;
    
            hr = m_pPkgCollection->SaveChanges(&lChanges);
            if (FAILED(hr))
            {
                SETUP_TRACE_ERROR((
                    DBG_CONTEXT, 
                    "Failed in m_pPkgCollection(%p)->SaveChanges. error = %08x\n",
                    m_pPkgCollection,
                    hr
                    ));
                goto LErrExit;
            }
        } 
    }
    
LErrExit:

    if (aCLSIDs != NULL)
        {
        SafeArrayDestroy(aCLSIDs);        
        aCLSIDs = NULL;
        }
        
    if (FAILED(hr))
        {
        SETUP_TRACE_ERROR((
            DBG_CONTEXT, 
            "Failed to Create Package. Package Name = %S, Package ID = %S, error = %08x\n",
            szPackageName,
            szPackageID,
            hr
            ));
        }
    
    SETUP_TRACE_ASSERT(SUCCEEDED(hr));

    ReleaseAll();
    
    SETUP_TRACE(( 
        DBG_CONTEXT, 
        "RETURN - CreatePackage ID(%S) Name(%S)\n",
        szPackageID,
        szPackageName
        ));

    return hr;
    }

/*===================================================================
AddComponentFromPackage    

Add a Component (a WAM CLSID) from a Viper Package.  Assume the package
is already existed.

Parameter:
szPackageID:            [in] Viper Package ID.
szComponentCLSID:        [in] Component CLSID.
fInProc:                [in] if TRUE, we set certain property on the Component.

Return:        HRESULT
Side Affect:
NONE.

===================================================================*/
HRESULT    WamRegPackageConfig::AddComponentToPackage
(    
IN LPCWSTR    szPackageID,
IN LPCWSTR    szComponentCLSID
)
{
    HRESULT            hr;
    BSTR bstrGUID    = NULL;
    BSTR bstr = NULL;
    VARIANT         varKey;
    long            lChanges;
    BOOL            fFound;
    long            lPkgCount;
    BOOL            fImported = FALSE;
    
    SETUP_TRACE(( 
        DBG_CONTEXT, 
        "CALL - AddComponentToPackage, Package(%S) Component(%S)\n",
        szPackageID,
        szComponentCLSID
        ));

    DBG_ASSERT(szPackageID);
    DBG_ASSERT(szComponentCLSID);
    
    VariantInit(&varKey);
    VariantClear(&varKey);

    DBG_ASSERT( m_pCatalog != NULL);
    DBG_ASSERT( m_pPkgCollection != NULL);

    varKey.vt = VT_BSTR;
    varKey.bstrVal = SysAllocString(szPackageID);
    
    bstr = SysAllocString(szPackageID);
    bstrGUID = SysAllocString(szComponentCLSID);
    
    hr = m_pCatalog->ImportComponent(bstr, bstrGUID);
    FREEBSTR(bstr);
    FREEBSTR(bstrGUID);
    if (FAILED(hr))
        {
        SETUP_TRACE_ERROR((
            DBG_CONTEXT, 
            "Failed in m_pCatalog(%p)->ImportComponent(). error %08x\n",
            m_pCatalog,
            hr
            ));
        goto LErrExit;
        }
    else
        {
        fImported = TRUE;
        }

    // Get the "ComponentsInPackage" collection.
    bstr = SysAllocString(L"Components");
    
    hr = m_pPkgCollection->GetCollection(bstr, varKey, (IDispatch**)&m_pCompCollection);
    FREEBSTR(bstr);
    VariantClear(&varKey);
    if (FAILED(hr))
        {
        SETUP_TRACE_ERROR((
            DBG_CONTEXT, 
            "Failed in m_pPkgCollection(%p)->GetCollection(). error %08x\n",
            m_pPkgCollection,
            hr
            ));
        goto LErrExit;
        }    

    //
    // Find and Set properties on the component object
    //
    hr = SetComponentObjectProperties( szComponentCLSID);
    if ( FAILED(hr)) 
    {
        SETUP_TRACE_ERROR((
            DBG_CONTEXT, 
            "Failed to SetComponentObjectProperties. error %08x\n",
            hr
            ));
        goto LErrExit;
    }
        
LErrExit:
        
    // Save changes
    if (SUCCEEDED(hr))
        {
        hr = m_pCompCollection->SaveChanges(&lChanges);
        if (FAILED(hr))
            {
            SETUP_TRACE_ERROR((
                DBG_CONTEXT, 
                "Failed in m_pCompCollection(%p)->SaveChanges(), error = %08x\n",
                m_pCompCollection,
                hr
                ));
            }
        }
    else
        {
        // CODEWORK - This seems like a bad idea. The release should drop any
        // changes we made, so this cleanup code seems to be asking for trouble.

        // Need to remove component from the package
        if (fImported && m_pCompCollection )
            {
            SETUP_TRACE_ERROR((
                DBG_CONTEXT, 
                "Failed in AddComponentToPackage, removing the component, error = %08x\n",
                hr
                ));

            HRESULT hrT;
            long    lCompCount;

            // Find our component in the list (should be the only one)
            hrT = m_pCompCollection->get_Count(&lCompCount);
            if (SUCCEEDED(hrT))
                {
                fFound = FALSE;
                if (SUCCEEDED(hrT) && lCompCount == 1)
                    {
                    // Found it
                    fFound = TRUE;
                    hrT = m_pCompCollection->Remove(0);
                    if (SUCCEEDED(hrT))
                        {
                        hrT = m_pCompCollection->SaveChanges(&lChanges);
                        if (FAILED(hrT))
                            {
                            SETUP_TRACE_ERROR((
                                DBG_CONTEXT, 
                                "Failed in m_pCompCollection->SaveChanges() during cleanup, error = %08x\n",
                                hrT
                                ));
                            }

                        }
                    else
                        {
                        SETUP_TRACE_ERROR((
                            DBG_CONTEXT, 
                            "Failed in m_pCompCollection->Remove() during cleanup, hr = %08x\n", 
                            hrT
                            ));
                        }
                    }
                }
            }
        }
        
    FREEBSTR(bstr);
    VariantClear(&varKey);
    
    ReleaseAll();

    SETUP_TRACE(( 
        DBG_CONTEXT, 
        "RETURN - AddComponentToPackage, Package(%S) Component(%S), hr=%08x\n",
        szPackageID,
        szComponentCLSID,
        hr
        ));
        
    return hr;
}

/*===================================================================
RemoveComponentFromPackage    

Remove a Component (a WAM CLSID) from a Viper Package.

Parameter:
szPackageID:            [in] Viper Package ID.
szComponentCLSID:        [in] Component CLSID.
fDeletePackage:            [in] if TRUE, we delete the package always. (be very careful, with in-proc
                             package).

Return:        HRESULT
Side Affect:
After remove the component from the package, if the component count in the
package is 0, then delete the whole package.

===================================================================*/
HRESULT    WamRegPackageConfig::RemoveComponentFromPackage
(
IN LPCWSTR szPackageID,
IN LPCWSTR szComponentCLSID,
IN DWORD   dwAppIsolated
)
{    
    HRESULT             hr;
    BSTR                bstr = NULL;
    BSTR                bstrGUID    = NULL;
    VARIANT             varKey;
    VARIANT             varT;
    SAFEARRAY*          aCLSIDs = NULL;
    LONG                Indices[1];
    long                lPkgCount, lCompCount, lChanges;
    long                lPkgIndex = 0;
    BOOL fFound;
    
    VariantInit(&varKey);
    VariantClear(&varKey);
    VariantInit(&varT);
    VariantClear(&varT);

    DBG_ASSERT( m_pCatalog != NULL);
    DBG_ASSERT( m_pPkgCollection != NULL);

    hr = GetSafeArrayOfCLSIDs(szPackageID, &aCLSIDs);
    //
    // Populate it
    //    
    hr = m_pPkgCollection->PopulateByKey(aCLSIDs);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call PopulateByKey(), hr = %08x\n",
            hr));
        goto LErrExit;
        }

    // Find our component in the list (should be the only one)
    hr = m_pPkgCollection->get_Count(&lPkgCount);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call MTS Admin API. error %08x\n", hr));
        goto LErrExit;
        }

    fFound = FALSE;
    if (SUCCEEDED(hr) && lPkgCount == 1)
        {
        hr = m_pPkgCollection->get_Item(0, (IDispatch**)&m_pPackage);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to call MTS Admin API. error %08x\n", hr));
            goto LErrExit;
            }
        
        hr = m_pPackage->get_Key(&varKey);
        if (SUCCEEDED(hr))
            {
            // Found it
            DBG_ASSERT(m_pPackage);
            fFound = TRUE;
            }
        }

    // Get the "Components" collection.
    bstr = SysAllocString(L"Components");
    hr = m_pPkgCollection->GetCollection(bstr, varKey, (IDispatch**)&m_pCompCollection);
    FREEBSTR(bstr);
    VariantClear(&varKey);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
        goto LErrExit;
        }
    
    // Repopulate the collection so we can find our object and set properties on it
    Indices[0] = 0;
    VariantInit(&varT);
    varT.vt = VT_BSTR;
    varT.bstrVal = SysAllocString(szComponentCLSID);
    hr = SafeArrayPutElement(aCLSIDs, Indices, &varT);
    VariantClear(&varT);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call SafeArrayDestroy(aCLSIDs), hr = %08x\n",
            hr));
        }
    //
    // Populate it
    //    
    hr = m_pCompCollection->PopulateByKey(aCLSIDs);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call PopulateByKey(), hr = %08x\n",
            hr));
        goto LErrExit;
        }

    // Find our component in the list (should be the only one)
    hr = m_pCompCollection->get_Count(&lCompCount);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to call MTS Admin API. error %08x\n", hr));
        goto LErrExit;
        }

    fFound = FALSE;
    if (SUCCEEDED(hr) && lCompCount == 1)
        {
        // Found it
        fFound = TRUE;
        hr = m_pCompCollection->Remove(0);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
            goto LErrExit;
            }
        }
        
    DBG_ASSERT(fFound);

    // Save changes
    hr = m_pCompCollection->SaveChanges(&lChanges);
    if (FAILED(hr))
        {
        DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
        goto LErrExit;
        }


    // 
    //  Need to populate again to get the Component count after remove the component from
    //  the package.  The populatebykey only populate 1 component a time.
    //  However, if this package is the default package hosting all in-proc WAM components,
    //  we know that there is at least one component W3SVC always in this package, therefore
    //  we skip the GetComponentCount call here.
    //  The component count for the default package must be at least one, 
    //

    // Set lCompCount = 1, so that the only case that lCompCount becomes 0 is the OutProc
    // Islated package has 0 components.
    lCompCount = 1;
    if (dwAppIsolated == static_cast<DWORD>(eAppRunOutProcIsolated))
        {
        hr = m_pCompCollection->Populate();
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
            goto LErrExit;
            }
            
        // Find our component in the list (should be the only one)
        hr = m_pCompCollection->get_Count(&lCompCount);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
            goto LErrExit;
            }

        // Component count is 0, remove the package.
        if (lCompCount == 0)
            {        
            // Found it - remove it and call Save Changes
            // First, Set Deleteable = Y property on package
            hr = SetPackageObjectProperty(L"Deleteable", L"Y");
            if (FAILED(hr))
                {
                goto LErrExit;
                }

            RELEASE(m_pPackage);
            // Let save the Deletable settings
            hr = m_pPkgCollection->SaveChanges(&lChanges);
            if (FAILED(hr))
                {
                DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
                goto LErrExit;
                }
                
            hr = m_pPkgCollection->Remove(0);
            if (FAILED(hr))
                {
                DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
                goto LErrExit;
                }
            }
        else
            {
            // Set Attribute Deleteable = "Y"
            hr = SetPackageObjectProperty(L"Deleteable", L"Y");
            if (FAILED(hr))
                {
                goto LErrExit;
                }

            // Set CreatedBy = ""
            hr = SetPackageObjectProperty(L"CreatedBy", L"");
            if (FAILED(hr))
                {
                goto LErrExit;
                }

            // Set Identity to Interactive User. MTS might use that package with "Interactive User"
            // as the indentity.
            hr = SetPackageObjectProperty(L"Identity", L"Interactive User");
            if (FAILED(hr))
                {
                DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
                goto LErrExit;
                }

            RELEASE(m_pPackage);
            }
            
        hr = m_pPkgCollection->SaveChanges(&lChanges);
        if (FAILED(hr))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to Call MTS Admin API, hr = %08x\n", hr));
            goto LErrExit;
            }
        }
LErrExit:

    if (aCLSIDs != NULL)
        {
        HRESULT hrT;
        hrT = SafeArrayDestroy(aCLSIDs);

        if (FAILED(hrT))
            {
            DBGPRINTF((DBG_CONTEXT, "Failed to call SafeArrayDestroy(aCLSIDs), hr = %08x\n",
                hr));
            }

        aCLSIDs = NULL;
        }

    FREEBSTR(bstr);
    
    VariantClear(&varKey);
    VariantClear(&varT);

    ReleaseAll();
    return hr;

}