#include "stdafx.h"
#include "wolfpack.h"

#ifndef _CHICAGO_
#include    <windows.h>
#include    <stdio.h>
#include    <clusapi.h>
#include    <resapi.h>
#include    <helper.h>

#define INITIAL_RESOURCE_NAME_SIZE 256 // In characters not in bytes
#define IIS_RESOURCE_TYPE_NAME L"IIS Server Instance"
#define SMTP_RESOURCE_TYPE_NAME L"SMTP Server Instance"
#define NNTP_RESOURCE_TYPE_NAME L"NNTP Server Instance"

#define MAX_OFFLINE_RETRIES 5 // Number of times to try and take a resources offline before giving up 
#define DELAY_BETWEEN_CALLS_TO_OFFLINE 1000*2 // in milliseconds

CONST LPCWSTR scClusterPath = _T("System\\CurrentControlSet\\Services\\ClusSvc");
CONST LPCWSTR scClusterPath2 = _T("System\\CurrentControlSet\\Services\\ClusSvc\\Parameters");

CStringList gcstrListOfClusResources;

int g_ClusterSVCExist = -1; // -1 = not checked, 1 = exist, 0 = not exist

typedef DWORD
(WINAPI *PFN_RESUTILFINDSZPROPERTY)(
IN LPVOID lpTheProperty,
IN OUT LPDWORD nInBufferSize,
IN LPCWSTR lpszResourceTypeName,
OUT LPVOID lpOutBuffer);

typedef DWORD
(WINAPI *PFN_RESUTILFINDDWORDPROPERTY)(
IN LPVOID lpTheProperty,
IN OUT LPDWORD nInBufferSize,
IN LPCWSTR lpszResourceTypeName,
OUT LPDWORD pdwPropertyValue);

typedef DWORD
(WINAPI *PFN_CLUSTERRESOURCECONTROL)(
IN HRESOURCE hResource,
IN HNODE hNode,
IN DWORD dwControlCode,
IN LPVOID lpInBuffer,
IN OUT DWORD nInBufferSize,
OUT LPVOID lpOutBuffer,
IN OUT DWORD nOutBufferSize,
OUT LPDWORD lpBytesReturned
);


typedef HCLUSTER
(WINAPI *PFN_OPENCLUSTER)(
    IN LPCWSTR lpszClusterName
    );

typedef BOOL
(WINAPI *PFN_CLOSECLUSTER)(
    IN HCLUSTER hCluster
    );

typedef DWORD
(WINAPI *PFN_CREATECLUSTERRESOURCETYPE)(
    IN HCLUSTER hCluster,
    IN LPCWSTR lpszResourceTypeName,
    IN LPCWSTR lpszDisplayName,
    IN LPCWSTR lpszResourceTypeDll,
    IN DWORD dwLooksAlivePollInterval,
    IN DWORD dwIsAlivePollInterval
    );

typedef DWORD
(WINAPI *PFN_DELETECLUSTERRESOURCETYPE)(
    IN HCLUSTER hCluster,
    IN LPCWSTR lpszResourceTypeName
    );

typedef HCLUSENUM
(WINAPI
*PFN_ClusterOpenEnum)(
    IN HCLUSTER hCluster,
    IN DWORD dwType
    );

typedef DWORD
(WINAPI
*PFN_ClusterEnum)(
    IN HCLUSENUM hEnum,
    IN DWORD dwIndex,
    OUT LPDWORD lpdwType,
    OUT LPWSTR lpszName,
    IN OUT LPDWORD lpcbName
    );

typedef DWORD
(WINAPI
*PFN_ClusterCloseEnum)(
    IN HCLUSENUM hEnum
    );

typedef HRESOURCE
(WINAPI
*PFN_OpenClusterResource)(
    IN HCLUSTER hCluster,
    IN LPCWSTR lpszResourceName
    );

typedef BOOL
(WINAPI
*PFN_CloseClusterResource)(
    IN HRESOURCE hResource
    );

typedef DWORD
(WINAPI
*PFN_DeleteClusterResource)(
    IN HRESOURCE hResource
    );

typedef DWORD
(WINAPI
*PFN_OfflineClusterResource)(
    IN HRESOURCE hResource
    );

typedef HKEY
(WINAPI
*PFN_GetClusterResourceKey)(
    IN HRESOURCE hResource,
    IN REGSAM samDesired
    );

typedef LONG
(WINAPI
*PFN_ClusterRegCloseKey)(
    IN HKEY hKey
    );

typedef LONG
(WINAPI
*PFN_ClusterRegQueryValue)(
    IN HKEY hKey,
    IN LPCWSTR lpszValueName,
    OUT LPDWORD lpValueType,
    OUT LPBYTE lpData,
    IN OUT LPDWORD lpcbData
    );

typedef CLUSTER_RESOURCE_STATE
(WINAPI
*PFN_GetClusterResourceState)(
    IN HRESOURCE hResource,
    OUT OPTIONAL LPWSTR lpszNodeName,
    IN OUT LPDWORD lpcbNodeName,
    OUT OPTIONAL LPWSTR lpszGroupName,
    IN OUT LPDWORD lpcbGroupName
    );

typedef DWORD
(WINAPI *PFN_DLLREGISTERCLUADMINEXTENSION)(
    IN HCLUSTER hCluster
    );

typedef DWORD
(WINAPI *PFN_DLLUNREGISTERCLUADMINEXTENSION)(
    IN HCLUSTER hCluster
    );


void ListOfClusResources_Add(TCHAR * szEntry)
{
    //Add entry to the list if not already there
    if (_tcsicmp(szEntry, _T("")) != 0)
    {
        // Add it if it is not already there.
        if (TRUE != IsThisStringInThisCStringList(gcstrListOfClusResources, szEntry))
        {
            gcstrListOfClusResources.AddTail(szEntry);
            //iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("ListOfClusResources_Add:%s\n"),szEntry));
        }
    }
    return;
}


INT ListOfClusResources_Check(TCHAR * szEntry)
{
    int iReturn = FALSE;

    //Add entry to the list if not already there
    if (_tcsicmp(szEntry, _T("")) != 0)
    {
        // Return true if it's in there!
        iReturn = IsThisStringInThisCStringList(gcstrListOfClusResources, szEntry);
    }
    return iReturn;
}



BOOL
RegisterIisServerInstanceResourceType(
    LPWSTR pszResType,
    LPWSTR pszResTypeDisplayName,
    LPWSTR pszPath,
    LPWSTR pszAdminPath
    )
{
    HCLUSTER                        hC;
    DWORD                           dwErr = ERROR_SUCCESS;
    HINSTANCE                       hClusapi;
    PFN_OPENCLUSTER                 pfnOpenCluster;
    PFN_CLOSECLUSTER                pfnCloseCluster;
    PFN_CREATECLUSTERRESOURCETYPE   pfnCreateClusterResourceType;
    HRESULT                         hres;

    if ( hClusapi = LoadLibrary( L"clusapi.dll" ) )
    {
        pfnOpenCluster = (PFN_OPENCLUSTER)GetProcAddress( hClusapi, "OpenCluster" );
        pfnCloseCluster = (PFN_CLOSECLUSTER)GetProcAddress( hClusapi, "CloseCluster" );
        pfnCreateClusterResourceType = (PFN_CREATECLUSTERRESOURCETYPE)GetProcAddress( hClusapi, "CreateClusterResourceType" );

        if ( pfnOpenCluster &&
             pfnCloseCluster &&
             pfnCreateClusterResourceType )
        {
            if ( hC = pfnOpenCluster( NULL ) )
            {
                hres = pfnCreateClusterResourceType(
                    hC,
                    pszResType,
                    pszResType,
                    pszPath,
                    5000,
                    60000 );


                if ( SUCCEEDED( hres ) )
                {
                    HINSTANCE                           hAdmin;
                    PFN_DLLREGISTERCLUADMINEXTENSION    pfnDllRegisterCluAdminExtension;

                    if ( hAdmin = LoadLibrary( pszAdminPath ) )
                    {
                        pfnDllRegisterCluAdminExtension =
                            (PFN_DLLREGISTERCLUADMINEXTENSION)GetProcAddress( hAdmin, "DllRegisterCluAdminExtension" );
                        if ( pfnDllRegisterCluAdminExtension )
                        {
                            if ( FAILED(hres = pfnDllRegisterCluAdminExtension( hC )) )
                            {
                                dwErr = hres;
                            }
                        }
                        else
                        {
                            dwErr = GetLastError();
                        }
                        FreeLibrary( hAdmin );
                    }
                    else
                    {
                        dwErr = GetLastError();
                    }
                }
                else
                {
                    dwErr = hres;
                }

                pfnCloseCluster( hC );

                if ( dwErr )
                {
                    SetLastError( dwErr );
                }
            }
        }
        else
        {
            dwErr = GetLastError();
        }

        FreeLibrary( hClusapi );
    }
    else
    {
        dwErr = GetLastError();
    }

    return dwErr == ERROR_SUCCESS ? TRUE : FALSE;
}


BOOL
UnregisterIisServerInstanceResourceType(
    LPWSTR pszResType,
    LPWSTR pszAdminPath,
    BOOL   bGrabVRootFromResourceAndAddToIISVRoot,
    BOOL   bDeleteAfterMove
    )
{
    CStringArray cstrArryName, cstrArryPath;
    CStringArray cstrArryNameftp, cstrArryPathftp;

    HCLUSTER                        hC;
    DWORD                           dwErr = ERROR_SUCCESS;
    HINSTANCE                       hClusapi;
    PFN_OPENCLUSTER                 pfnOpenCluster;
    PFN_CLOSECLUSTER                pfnCloseCluster;
    PFN_DELETECLUSTERRESOURCETYPE   pfnDeleteClusterResourceType;
    PFN_ClusterOpenEnum             pfnClusterOpenEnum;
    PFN_ClusterEnum                 pfnClusterEnum;
    PFN_ClusterCloseEnum            pfnClusterCloseEnum;
    PFN_OpenClusterResource         pfnOpenClusterResource;
    PFN_CloseClusterResource        pfnCloseClusterResource;
    PFN_DeleteClusterResource       pfnDeleteClusterResource;
    PFN_OfflineClusterResource      pfnOfflineClusterResource;
    PFN_GetClusterResourceKey       pfnGetClusterResourceKey;
    PFN_ClusterRegCloseKey          pfnClusterRegCloseKey;
    PFN_ClusterRegQueryValue        pfnClusterRegQueryValue;
    PFN_GetClusterResourceState     pfnGetClusterResourceState;
    HRESULT                         hres;
    HCLUSENUM                       hClusEnum;
    WCHAR *                         pawchResName = NULL;
    WCHAR                           awchResName[256];
    WCHAR                           awchResType[256];
    DWORD                           dwEnum;
    DWORD                           dwType;
    DWORD                           dwStrLen;
    HRESOURCE                       hRes;
    HKEY                            hKey;
    BOOL                            fDel;
    DWORD                           dwRetry;

    hClusapi = NULL;
    hClusapi = LoadLibrary(L"clusapi.dll");
    if (!hClusapi)
    {
        hClusapi = NULL;
        iisDebugOut((LOG_TYPE_ERROR, _T("UnregisterIisServerInstanceResourceType:LoadLib clusapi.dll failed.\n")));
        goto UnregisterIisServerInstanceResourceType_Exit;
    }
    pfnOpenCluster = (PFN_OPENCLUSTER)GetProcAddress( hClusapi, "OpenCluster" );
    pfnCloseCluster = (PFN_CLOSECLUSTER)GetProcAddress( hClusapi, "CloseCluster" );
    pfnDeleteClusterResourceType = (PFN_DELETECLUSTERRESOURCETYPE)GetProcAddress( hClusapi, "DeleteClusterResourceType" );
    pfnClusterOpenEnum = (PFN_ClusterOpenEnum)GetProcAddress( hClusapi, "ClusterOpenEnum" );
    pfnClusterEnum = (PFN_ClusterEnum)GetProcAddress( hClusapi, "ClusterEnum" );
    pfnClusterCloseEnum = (PFN_ClusterCloseEnum)GetProcAddress( hClusapi, "ClusterCloseEnum" );
    pfnOpenClusterResource = (PFN_OpenClusterResource)GetProcAddress( hClusapi, "OpenClusterResource" );
    pfnCloseClusterResource = (PFN_CloseClusterResource)GetProcAddress( hClusapi, "CloseClusterResource" );
    pfnDeleteClusterResource = (PFN_DeleteClusterResource)GetProcAddress( hClusapi, "DeleteClusterResource" );
    pfnOfflineClusterResource = (PFN_OfflineClusterResource)GetProcAddress( hClusapi, "OfflineClusterResource" );
    pfnGetClusterResourceKey = (PFN_GetClusterResourceKey)GetProcAddress( hClusapi, "GetClusterResourceKey" );
    pfnClusterRegCloseKey = (PFN_ClusterRegCloseKey)GetProcAddress( hClusapi, "ClusterRegCloseKey" );
    pfnClusterRegQueryValue = (PFN_ClusterRegQueryValue)GetProcAddress( hClusapi, "ClusterRegQueryValue" );
    pfnGetClusterResourceState = (PFN_GetClusterResourceState)GetProcAddress( hClusapi, "GetClusterResourceState" );

    if ( !pfnOpenCluster ||
         !pfnCloseCluster ||
         !pfnDeleteClusterResourceType ||
         !pfnClusterOpenEnum ||
         !pfnClusterEnum ||
         !pfnClusterCloseEnum ||
         !pfnOpenClusterResource ||
         !pfnCloseClusterResource ||
         !pfnDeleteClusterResource ||
         !pfnOfflineClusterResource ||
         !pfnGetClusterResourceKey  ||
         !pfnClusterRegCloseKey ||
         !pfnClusterRegQueryValue ||
         !pfnGetClusterResourceState )
    {
        iisDebugOut((LOG_TYPE_ERROR, _T("UnregisterIisServerInstanceResourceType:clusapi.dll missing export function.failure.\n")));
        goto UnregisterIisServerInstanceResourceType_Exit;
    }

    hC = pfnOpenCluster(NULL);
    // if we can't open the cluster, then maybe there are none.
    if (!hC) {goto UnregisterIisServerInstanceResourceType_Exit;}

    // Delete all resources of type pszResType
    hClusEnum = pfnClusterOpenEnum(hC, CLUSTER_ENUM_RESOURCE);
    if (hClusEnum != NULL)
    {
        dwEnum = 0;
        int iClusterEnumReturn = ERROR_SUCCESS;

        // allocate the initial buffer for pawchResName
        dwStrLen = 256 * sizeof(WCHAR);
        pawchResName = NULL;
        pawchResName = (WCHAR *) malloc( dwStrLen );
        if (!pawchResName)
        {
            iisDebugOut((LOG_TYPE_ERROR, _T("UnregisterIisServerInstanceResourceType: malloc FAILED.out of memory.\n")));
            goto UnregisterIisServerInstanceResourceType_Exit;
        }

        do
        {
            iClusterEnumReturn = ERROR_SUCCESS;
            iClusterEnumReturn = pfnClusterEnum( hClusEnum, dwEnum, &dwType, pawchResName, &dwStrLen );
            if (iClusterEnumReturn != ERROR_SUCCESS)
            {
                // Check if failed because it needs more space.
                if (iClusterEnumReturn == ERROR_MORE_DATA)
                {
	                // dwStrLen should be set to the required length returned from pfnClusterEnum
                    dwStrLen = (dwStrLen + 1) * sizeof(WCHAR);
                    pawchResName = (WCHAR *) realloc(pawchResName, dwStrLen);
                    if (!pawchResName)
                    {
                        iisDebugOut((LOG_TYPE_ERROR, _T("UnregisterIisServerInstanceResourceType: realloc FAILED.out of memory.\n")));
                        goto UnregisterIisServerInstanceResourceType_Exit;
                    }
                    // try it again.
                    iClusterEnumReturn = ERROR_SUCCESS;
                    iClusterEnumReturn = pfnClusterEnum( hClusEnum, dwEnum, &dwType, pawchResName, &dwStrLen );
                    if (iClusterEnumReturn != ERROR_SUCCESS)
                    {
                        iisDebugOut((LOG_TYPE_ERROR, _T("UnregisterIisServerInstanceResourceType: FAILED.err=0x%x.\n"), iClusterEnumReturn));
                        break;
                    }
                }
                else
                {
                    if (iClusterEnumReturn != ERROR_NO_MORE_ITEMS)
                    {
                        // failed for some other reason than no more data
                        iisDebugOut((LOG_TYPE_ERROR, _T("UnregisterIisServerInstanceResourceType: FAILED.err=0x%x.\n"), iClusterEnumReturn));
                    }
                    break;
                }
            }


            // proceed
            if ( hRes = pfnOpenClusterResource( hC, pawchResName ) )
            {
                if ( hKey = pfnGetClusterResourceKey( hRes, KEY_READ ) )
                {
                    dwStrLen = sizeof(awchResType)/sizeof(WCHAR);
                    // Check if it's for 'our' type of key (pszResType)
                    fDel = pfnClusterRegQueryValue( hKey, L"Type", &dwType, (LPBYTE)awchResType, &dwStrLen ) == ERROR_SUCCESS && !wcscmp( awchResType, pszResType );
                    pfnClusterRegCloseKey( hKey );

                    if ( fDel )
                    {
                        if (bDeleteAfterMove)
                        {
                            // Take the resource off line so that we can actually delete it, i guess.
                            pfnOfflineClusterResource( hRes );
                            for ( dwRetry = 0 ;dwRetry < 30 && pfnGetClusterResourceState( hRes,NULL,&dwStrLen,NULL,&dwStrLen ) != ClusterResourceOffline; ++dwRetry )
                            {
                                Sleep( 1000 );
                            }
                        }

                        // At this point we have successfully got the cluster to go offline
                        if (bGrabVRootFromResourceAndAddToIISVRoot)
                        {
                            // At this point we have successfully got the cluster to go offline

                            // Get the vroot names and path's here and stick into the arrays....
                            GetClusterIISVRoot(hRes, L"W3SVC", cstrArryName, cstrArryPath);

                            // Do it for FTP now.
                            GetClusterIISVRoot(hRes, L"MSFTPSVC", cstrArryNameftp, cstrArryPathftp);

                            // No need to do it for gopher since there is none.
                            //GetClusterIISVRoot(hRes, L"GOPHERSVC", cstrArryName, cstrArryPath);
                        }

                        // We have saved all the important data into our Array's
                        // now it's okay to delete the Resource
                        if (bDeleteAfterMove)
                        {
                            pfnDeleteClusterResource( hRes );
                        }
                    }
                }

                pfnCloseClusterResource( hRes );
            }

            // Increment to the next one
            ++dwEnum;

        } while(TRUE);

        pfnClusterCloseEnum( hClusEnum );
    }

    if (bDeleteAfterMove)
    {
        dwErr = pfnDeleteClusterResourceType(hC,pszResType );

        HINSTANCE hAdmin;
        if ( hAdmin = LoadLibrary( pszAdminPath ) )
        {
            PFN_DLLUNREGISTERCLUADMINEXTENSION  pfnDllUnregisterCluAdminExtension;
            pfnDllUnregisterCluAdminExtension = (PFN_DLLUNREGISTERCLUADMINEXTENSION)GetProcAddress( hAdmin, "DllUnregisterCluAdminExtension" );
            if ( pfnDllUnregisterCluAdminExtension )
            {
                if ( FAILED(hres = pfnDllUnregisterCluAdminExtension( hC )) )
                {
                    dwErr = hres;
                }
            }
            else
            {
                dwErr = GetLastError();
            }
            FreeLibrary( hAdmin );
        }
        else
        {
            dwErr = GetLastError();
        }
    }

    pfnCloseCluster( hC );

    if (dwErr)
        {SetLastError( dwErr );}

UnregisterIisServerInstanceResourceType_Exit:
    // Copy these to the iis virtual root registry....
    MoveVRootToIIS3Registry(REG_W3SVC,cstrArryName,cstrArryPath);
    // Copy these to the iis virtual root registry....
    MoveVRootToIIS3Registry(REG_MSFTPSVC,cstrArryNameftp,cstrArryPathftp);

    if (hClusapi) {FreeLibrary(hClusapi);}
    if (pawchResName) {free(pawchResName);}
    return dwErr == ERROR_SUCCESS ? TRUE : FALSE;
}


void TestClusterRead(LPWSTR pszClusterName)
{
    iisDebugOut_Start(_T("TestClusterRead"));

    LPWSTR pszResType = L"IIS Virtual Root";

    CStringArray cstrArryName, cstrArryPath;
    CStringArray cstrArryNameftp, cstrArryPathftp;

    HCLUSTER                        hC;
    DWORD                           dwErr = ERROR_SUCCESS;
    HINSTANCE                       hClusapi;
    PFN_OPENCLUSTER                 pfnOpenCluster;
    PFN_CLOSECLUSTER                pfnCloseCluster;
    PFN_DELETECLUSTERRESOURCETYPE   pfnDeleteClusterResourceType;
    PFN_ClusterOpenEnum             pfnClusterOpenEnum;
    PFN_ClusterEnum                 pfnClusterEnum;
    PFN_ClusterCloseEnum            pfnClusterCloseEnum;
    PFN_OpenClusterResource         pfnOpenClusterResource;
    PFN_CloseClusterResource        pfnCloseClusterResource;
    PFN_DeleteClusterResource       pfnDeleteClusterResource;
    PFN_OfflineClusterResource      pfnOfflineClusterResource;
    PFN_GetClusterResourceKey       pfnGetClusterResourceKey;
    PFN_ClusterRegCloseKey          pfnClusterRegCloseKey;
    PFN_ClusterRegQueryValue        pfnClusterRegQueryValue;
    PFN_GetClusterResourceState     pfnGetClusterResourceState;
    HRESULT                         hres;
    HCLUSENUM                       hClusEnum;
    WCHAR *                         pawchResName = NULL;
    WCHAR                           awchResName[256];
    WCHAR                           awchResType[256];
    DWORD                           dwEnum;
    DWORD                           dwType;
    DWORD                           dwStrLen;
    HRESOURCE                       hRes;
    HKEY                            hKey;
    BOOL                            fDel;
    DWORD                           dwRetry;

    hClusapi = NULL;
    hClusapi = LoadLibrary(L"clusapi.dll");
    if (!hClusapi)
    {
        hClusapi = NULL;
        iisDebugOut((LOG_TYPE_TRACE, _T("fail 1\n")));
        goto TestClusterRead_Exit;
    }
    pfnOpenCluster = (PFN_OPENCLUSTER)GetProcAddress( hClusapi, "OpenCluster" );
    pfnCloseCluster = (PFN_CLOSECLUSTER)GetProcAddress( hClusapi, "CloseCluster" );
    pfnDeleteClusterResourceType = (PFN_DELETECLUSTERRESOURCETYPE)GetProcAddress( hClusapi, "DeleteClusterResourceType" );
    pfnClusterOpenEnum = (PFN_ClusterOpenEnum)GetProcAddress( hClusapi, "ClusterOpenEnum" );
    pfnClusterEnum = (PFN_ClusterEnum)GetProcAddress( hClusapi, "ClusterEnum" );
    pfnClusterCloseEnum = (PFN_ClusterCloseEnum)GetProcAddress( hClusapi, "ClusterCloseEnum" );
    pfnOpenClusterResource = (PFN_OpenClusterResource)GetProcAddress( hClusapi, "OpenClusterResource" );
    pfnCloseClusterResource = (PFN_CloseClusterResource)GetProcAddress( hClusapi, "CloseClusterResource" );
    pfnDeleteClusterResource = (PFN_DeleteClusterResource)GetProcAddress( hClusapi, "DeleteClusterResource" );
    pfnOfflineClusterResource = (PFN_OfflineClusterResource)GetProcAddress( hClusapi, "OfflineClusterResource" );
    pfnGetClusterResourceKey = (PFN_GetClusterResourceKey)GetProcAddress( hClusapi, "GetClusterResourceKey" );
    pfnClusterRegCloseKey = (PFN_ClusterRegCloseKey)GetProcAddress( hClusapi, "ClusterRegCloseKey" );
    pfnClusterRegQueryValue = (PFN_ClusterRegQueryValue)GetProcAddress( hClusapi, "ClusterRegQueryValue" );
    pfnGetClusterResourceState = (PFN_GetClusterResourceState)GetProcAddress( hClusapi, "GetClusterResourceState" );

    if ( !pfnOpenCluster ||
         !pfnCloseCluster ||
         !pfnDeleteClusterResourceType ||
         !pfnClusterOpenEnum ||
         !pfnClusterEnum ||
         !pfnClusterCloseEnum ||
         !pfnOpenClusterResource ||
         !pfnCloseClusterResource ||
         !pfnDeleteClusterResource ||
         !pfnOfflineClusterResource ||
         !pfnGetClusterResourceKey  ||
         !pfnClusterRegCloseKey ||
         !pfnClusterRegQueryValue ||
         !pfnGetClusterResourceState )
    {
        iisDebugOut((LOG_TYPE_TRACE, _T("fail 2\n")));
        goto TestClusterRead_Exit;
    }

    iisDebugOut((LOG_TYPE_TRACE, _T("try to open cluster=%s\n"),pszClusterName));
    // try to open the cluster on the computer
    if ( hC = pfnOpenCluster( pszClusterName ) )
    {
        //
        // Delete all resources of type pszResType
        //
        if ( (hClusEnum = pfnClusterOpenEnum( hC, CLUSTER_ENUM_RESOURCE )) != NULL )
        {
            dwEnum = 0;
            int iClusterEnumReturn = ERROR_SUCCESS;

            // allocate the initial buffer for pawchResName
            dwStrLen = 256 * sizeof(WCHAR);
            pawchResName = NULL;
            pawchResName = (LPTSTR) malloc( dwStrLen );
            if (!pawchResName)
            {
                iisDebugOut((LOG_TYPE_ERROR, _T("TestClusterRead: malloc FAILED.out of memory.\n")));
                goto TestClusterRead_Exit;
            }

            do
            {
                iClusterEnumReturn = pfnClusterEnum( hClusEnum, dwEnum, &dwType, pawchResName, &dwStrLen );
                if (iClusterEnumReturn != ERROR_SUCCESS)
                {
                    // Check if failed because it needs more space.
                    if (iClusterEnumReturn == ERROR_MORE_DATA)
                    {
                        // dwStrLen should be set to the required length returned from pfnClusterEnum
                        dwStrLen = (dwStrLen + 1) * sizeof(WCHAR);
                        pawchResName = (LPTSTR) realloc(pawchResName, dwStrLen);
                        if (!pawchResName)
                        {
                            iisDebugOut((LOG_TYPE_ERROR, _T("TestClusterRead: realloc FAILED.out of memory.\n")));
                            goto TestClusterRead_Exit;
                        }
                        // try it again.
                        iClusterEnumReturn = pfnClusterEnum( hClusEnum, dwEnum, &dwType, pawchResName, &dwStrLen );
                        if (iClusterEnumReturn != ERROR_SUCCESS)
                        {
                            iisDebugOut((LOG_TYPE_ERROR, _T("TestClusterRead: FAILED.err=0x%x.\n"), iClusterEnumReturn));
                            break;
                        }
                    }
                    else
                    {
                        if (iClusterEnumReturn != ERROR_NO_MORE_ITEMS)
                        {
                            // failed for some other reason.
                            iisDebugOut((LOG_TYPE_ERROR, _T("TestClusterRead: FAILED.err=0x%x.\n"), iClusterEnumReturn));
                        }
                        break;
                    }
                }

                // proceed
                if ( hRes = pfnOpenClusterResource( hC, pawchResName ) )
                {
                    if ( hKey = pfnGetClusterResourceKey( hRes, KEY_READ ) )
                    {
                        dwStrLen = sizeof(awchResType)/sizeof(WCHAR);

                        fDel = pfnClusterRegQueryValue( hKey, L"Type", &dwType, (LPBYTE)awchResType, &dwStrLen ) == ERROR_SUCCESS && !wcscmp( awchResType, pszResType );

                        iisDebugOut((LOG_TYPE_TRACE, _T("TestClusterRead():ClusterRegQueryValue:%s."),awchResType));
                        pfnClusterRegCloseKey( hKey );

                        if ( fDel )
                        {
                            /*
                            pfnOfflineClusterResource( hRes );
                            for ( dwRetry = 0 ; dwRetry < 30 && pfnGetClusterResourceState( hRes,NULL,&dwStrLen,NULL,&dwStrLen ) != ClusterResourceOffline; ++dwRetry )
                            {
                                Sleep( 1000 );
                            }
                            */

                            // At this point we have successfully got the cluster to go offline

                            // Get the vroot names and path's here and stick into the arrays....
                            GetClusterIISVRoot(hRes, L"W3SVC", cstrArryName, cstrArryPath);

                            // Do it for FTP now.
                            GetClusterIISVRoot(hRes, L"MSFTPSVC", cstrArryNameftp, cstrArryPathftp);

                            // No need to do it for gopher since there is none.
                            //GetClusterIISVRoot(hRes, L"GOPHERSVC", cstrArryName, cstrArryPath);
                        }
                    }

                    pfnCloseClusterResource( hRes );
                }

                // Increment to the next one
                ++dwEnum;

            } while(TRUE);

            pfnClusterCloseEnum( hClusEnum );
        }

        //dwErr = pfnDeleteClusterResourceType(hC,pszResType );

        pfnCloseCluster( hC );

        if (dwErr)
            {SetLastError( dwErr );}
    }
    else
    {
        iisDebugOut((LOG_TYPE_TRACE, _T("fail 3\n")));
    }

TestClusterRead_Exit:
    // Copy these to the iis virtual root registry....
    MoveVRootToIIS3Registry(REG_W3SVC,cstrArryName,cstrArryPath);
    // Copy these to the iis virtual root registry....
    MoveVRootToIIS3Registry(REG_MSFTPSVC,cstrArryNameftp,cstrArryPathftp);

    if (hClusapi) {FreeLibrary(hClusapi);}
    if (pawchResName) {free(pawchResName);}
    iisDebugOut_End(_T("TestClusterRead"));
    return;
}

/****************************************************************************************
 *
 * Function: GetClusterIISVRoot
 *
 * Args: [in] hResource , the resource whos info should be added to the list
 *
 * Retrurn: GetLastError, on error
 *
 ****************************************************************************************/
int GetClusterIISVRoot(HRESOURCE hResource, LPWSTR pszTheServiceType, CStringArray &strArryOfVrootNames, CStringArray &strArryOfVrootData)
{
    //iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: start\n")));
    int iReturn = FALSE;
    HINSTANCE                       hClusapi;
    HINSTANCE                       hResutils;

    PFN_CLUSTERRESOURCECONTROL      pfnClusterResourceControl;
    PFN_RESUTILFINDSZPROPERTY       pfnResUtilFindSzProperty;
    PFN_RESUTILFINDDWORDPROPERTY    pfnResUtilFindDwordProperty;

	//
	// Initial size of the buffer
	//
	DWORD dwBufferSize = 256;
	
	//
	// The requested buffer size, and the number of bytes actually in the returned buffer
	//
	DWORD dwRequestedBufferSize = dwBufferSize;

	//
	// Result from the call to the cluster resource control function
	//
	DWORD dwResult;

	//
	// Buffer that holds the property list for this resource
	//
	LPVOID lpvPropList = NULL;

	//
	// The Proivate property that is being read
	//
	LPWSTR lpwszPrivateProp = NULL;

    hClusapi = LoadLibrary( L"clusapi.dll" );
    if (!hClusapi)
        {
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: failed to loadlib clusapi.dll\n")));
        goto GetIISVRoot_Exit;
        }

    pfnClusterResourceControl = (PFN_CLUSTERRESOURCECONTROL)GetProcAddress( hClusapi, "ClusterResourceControl" );
    if (!pfnClusterResourceControl)
        {
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: failed to GetProcAddress clusapi.dll:ClusterResourceControl\n")));
        goto GetIISVRoot_Exit;
        }

    hResutils = LoadLibrary( L"resutils.dll" );
    if (!hResutils)
        {
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: failed to loadlib resutils.dll\n")));
        goto GetIISVRoot_Exit;
        }
    pfnResUtilFindSzProperty = (PFN_RESUTILFINDSZPROPERTY)GetProcAddress( hResutils, "ResUtilFindSzProperty" );
    if (!pfnResUtilFindSzProperty)
        {
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: failed to GetProcAddress resutils.dll:ResUtilFindSzProperty\n")));
        goto GetIISVRoot_Exit;
        }
    pfnResUtilFindDwordProperty = (PFN_RESUTILFINDDWORDPROPERTY)GetProcAddress( hResutils, "ResUtilFindDwordProperty" );
    if (!pfnResUtilFindDwordProperty)
        {
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: failed to GetProcAddress resutils.dll:ResUtilFindDwordProperty\n")));
        goto GetIISVRoot_Exit;
        }

	//
	// Allocate memory for the resource type
	//
	lpvPropList = (LPWSTR) HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize * sizeof(WCHAR) );
	if( lpvPropList == NULL)
	{
		lpvPropList = NULL;
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: E_OUTOFMEMORY\n")));
        goto GetIISVRoot_Exit;
	}

	//
	// Allocate memory for the Property
	//
	lpwszPrivateProp = (LPWSTR) HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, (_MAX_PATH+_MAX_PATH+1) * sizeof(WCHAR) );
	if( lpwszPrivateProp == NULL)
	{
		lpvPropList = NULL;
        iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: E_OUTOFMEMORY\n")));
        goto GetIISVRoot_Exit;
	}
	
	//
	// Get the resource's private properties (Service , InstanceId)
	//
	while( 1 )
	{
		dwResult = pfnClusterResourceControl(hResource,NULL,CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES,NULL,0,lpvPropList,dwBufferSize,&dwRequestedBufferSize );
		if( ERROR_SUCCESS == dwResult )
		{
            // ---------------------
            // what the entries are:
            // AccessMask (dword) = 5
            // Alias (string)     = "virtual dir name"
            // Directory (string) = "c:\la\lalalala"
            // ServiceName (string) = W3SVC, MSFTPSVC, GOPHERSVC
            // ---------------------

            //
            // Get the "ServiceName" entry
            //
            dwResult = pfnResUtilFindSzProperty( lpvPropList, &dwRequestedBufferSize, L"ServiceName", &lpwszPrivateProp);
			if( dwResult != ERROR_SUCCESS )
			{
                iisDebugOut((LOG_TYPE_ERROR, _T("Couldn't get 'ServiceName' property.fail\n")));
                goto GetIISVRoot_Exit;
			}

            if (_wcsicmp(lpwszPrivateProp, pszTheServiceType) == 0)
            {
                // okay, we want to do stuff with this one!!!
                DWORD dwAccessMask;
                CString csAlias;
                CString csDirectory;

                TCHAR szMyBigPath[_MAX_PATH + 20];

                DWORD dwPrivateProp = 0;
                dwRequestedBufferSize = sizeof(DWORD);

                // get the Access Mask.
                dwResult = pfnResUtilFindDwordProperty( lpvPropList, &dwRequestedBufferSize, L"AccessMask", &dwPrivateProp);
			    if( dwResult != ERROR_SUCCESS )
			    {
                    iisDebugOut((LOG_TYPE_ERROR, _T("Couldn't get 'AccessMask' property.fail\n")));
                    goto GetIISVRoot_Exit;
			    }
                dwAccessMask = dwPrivateProp;

                // get the Alias
                dwResult = pfnResUtilFindSzProperty( lpvPropList, &dwRequestedBufferSize, L"Alias", &lpwszPrivateProp);
			    if( dwResult != ERROR_SUCCESS )
			    {
                    iisDebugOut((LOG_TYPE_ERROR, _T("Couldn't get 'Alias' property.fail\n")));
                    goto GetIISVRoot_Exit;
			    }
                csAlias = lpwszPrivateProp;

                // Get the Directory
                dwResult = pfnResUtilFindSzProperty( lpvPropList, &dwRequestedBufferSize, L"Directory", &lpwszPrivateProp);
			    if( dwResult != ERROR_SUCCESS )
			    {
                    iisDebugOut((LOG_TYPE_ERROR, _T("Couldn't get 'Directory' property.fail\n")));
                    goto GetIISVRoot_Exit;
			    }
                TCHAR thepath[_MAX_PATH];
                TCHAR * pmypath;
                csDirectory = lpwszPrivateProp;

                // make sure it's a valid directory name!
                if (0 != GetFullPathName(lpwszPrivateProp, _MAX_PATH, thepath, &pmypath))
                    {csDirectory = thepath;}

                // --------------------
                // formulate the string
                // --------------------

			    //
			    // Put the Name into the array.
			    //
                // "/Alias"
                strArryOfVrootNames.Add(csAlias);

                //
                // "C:\inetpub\ASPSamp,,5"
                //
                _stprintf(szMyBigPath,_T("%s,,%d"),csDirectory, dwAccessMask);
                strArryOfVrootData.Add(szMyBigPath);

                iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("Entry=[%s] '%s=%s'\n"),pszTheServiceType,csAlias,szMyBigPath));
            }
            goto GetIISVRoot_Exit;
		}

		if( ERROR_MORE_DATA == dwResult )
		{
			//
			// Set the buffer size to the required size reallocate the buffer
			//
			dwBufferSize = ++dwRequestedBufferSize;

			lpvPropList = (LPWSTR) HeapReAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, lpvPropList, dwBufferSize * sizeof(WCHAR) );
			if ( lpvPropList == NULL)
			{
                // out of memory!!!!
                goto GetIISVRoot_Exit;
			}
		}
	}

GetIISVRoot_Exit:
    if (lpwszPrivateProp)
        {HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, lpwszPrivateProp);}
    if (lpvPropList)
        {HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, lpvPropList);}
    //iisDebugOut((LOG_TYPE_ERROR, _T("GetClusterIISVRoot: end\n")));
    return iReturn;
}


// REG_W3SVC, REG_MSFTPSVC
void MoveVRootToIIS3Registry(CString strRegPath, CStringArray &strArryOfVrootNames, CStringArray &strArryOfVrootData)
{
    int nArrayItems = 0;
    int i = 0;

    strRegPath +=_T("\\Parameters\\Virtual Roots");
    CRegKey regVR( HKEY_LOCAL_MACHINE, strRegPath);
    if ((HKEY) regVR)
    {
        nArrayItems = (int)strArryOfVrootNames.GetSize();
        // if the CString arrays are empty then we won't ever process anything (nArrayItems is 1 based)
        for (i = 0; i < nArrayItems; i++ )
        {
            regVR.SetValue(strArryOfVrootNames[i], strArryOfVrootData[i]);
            iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("Array[%d]:%s=%s\n"),i,strArryOfVrootNames[i], strArryOfVrootData[i]));
        }
    }
    return;
}

void Upgrade_WolfPack()
{
    iisDebugOut_Start(_T("Upgrade_WolfPack"),LOG_TYPE_TRACE);

    CRegKey regClusSvc(HKEY_LOCAL_MACHINE, scClusterPath, KEY_READ);
    if ( (HKEY)regClusSvc )
    {
        CString csPath;
        TCHAR szPath[MAX_PATH];
        if (regClusSvc.QueryValue(_T("ImagePath"), csPath) == NERR_Success)
        {
			// string is formatted like this
			// %SystemRoot%\cluster\clusprxy.exe
			// Find the last \ and trim and paste the new file name on
			csPath = csPath.Left(csPath.ReverseFind('\\'));
			
			if ( csPath.IsEmpty() )
            {
				ASSERT(TRUE);
				return;
			}
			
            csPath += _T("\\iisclex3.dll");
	
	        if ( ExpandEnvironmentStrings( (LPCTSTR)csPath,szPath,sizeof(szPath)/sizeof(TCHAR)))
            {
                // in iis3.0 the resources were called 'IIS Virtual Root'
                // in iis4.0 it is something else (iis50 is the same as iis4)
	    	    UnregisterIisServerInstanceResourceType(L"IIS Virtual Root",(LPTSTR)szPath,TRUE,TRUE);
            }
            else
            {
				ASSERT(TRUE);
            }
        }

        ProcessSection(g_pTheApp->m_hInfHandle, _T("Wolfpack_Upgrade"));
    }
    iisDebugOut_End(_T("Upgrade_WolfPack"),LOG_TYPE_TRACE);
}


/****************************************************
*
* Known "problem": If a resource doesn't come offline after the five
* retries than the function continues to try to take the other iis resources
* offline but there is no error reported. You could change this pretty simply I think.
*
*****************************************************/
DWORD BringALLIISClusterResourcesOffline()
{
	//
	// The return code
	//
	DWORD dwError = ERROR_SUCCESS;
	
	//
	// Handle for the cluster
	//
	HCLUSTER hCluster = NULL;

	//
	// Handle for the cluster enumerator
	//
	HCLUSENUM hClusResEnum = NULL;

	//
	// Handle to a resource
	// 
	HRESOURCE hResource = NULL;

	//
	// The index of the resources we're taking offline
	//
	DWORD dwResourceIndex = 0;

	//
	// The type cluster object being enumerated returned by the ClusterEnum function
	//
	DWORD dwObjectType = 0;

	//
	// The name of the cluster resource returned by the ClusterEnum function
	//
	LPWSTR lpwszResourceName = NULL;
	
	//
	// The return code from the call to ClusterEnum
	//
	DWORD dwResultClusterEnum = ERROR_SUCCESS;

	//
	// The size of the buffer (in characters) that is used to hold the resource name's length
	//	
	DWORD dwResourceNameBufferLength = INITIAL_RESOURCE_NAME_SIZE;

	//
	// Size of the resource name passed to and returned by the ClusterEnum function
	//	
	DWORD dwClusterEnumResourceNameLength = dwResourceNameBufferLength;

    BOOL iClusDependsOnIISServices = FALSE;

	//
	// Open the cluster
	//
	if ( !(hCluster = OpenCluster(NULL)) )
	{
        dwError = GetLastError();
        // This will fail with RPC_S_SERVER_UNAVAILABLE "The RPC server is unavailable" if there is no cluster on this system
        if (hCluster == NULL)
        {
            if ( (dwError != RPC_S_SERVER_UNAVAILABLE) &&
                 (dwError != EPT_S_NOT_REGISTERED ) )
            {
                iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOffline:OpenCluster failed err=0x%x.\n"),dwError));
            }
        }
        else
        {
            iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOffline:OpenCluster failed err=0x%x.\n"),dwError));
        }
		goto clean_up;
	}

    iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("BringALLIISClusterResourcesOffline:start.\n")));

	//
	// Get Enumerator for the cluster resouces
	// 
	if ( !(hClusResEnum = ClusterOpenEnum( hCluster, CLUSTER_ENUM_RESOURCE )) )
	{
		dwError = GetLastError();
        iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOffline:ClusterOpenEnum failed err=0x%x.\n"),dwError));
		goto clean_up;	
	}
	
	//
	// Enumerate the Resources in the cluster
	// 
	
	//
	// Allocate memory to hold the cluster resource name as we enumerate the resources
	//
	if ( !(lpwszResourceName = (LPWSTR) LocalAlloc(LPTR, dwResourceNameBufferLength * sizeof(WCHAR))) )
	{
		dwError = GetLastError();
        iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOffline:LocalAlloc failed err=0x%x.\n"),dwError));
		goto clean_up;
	}

	// 
	// Enumerate all of the resources in the cluster and take the IIS Server Instance's offline
	//
	while( ERROR_NO_MORE_ITEMS  != 
	       (dwResultClusterEnum = ClusterEnum(hClusResEnum,
			              dwResourceIndex, 
				      &dwObjectType, 
				      lpwszResourceName,
				      &dwClusterEnumResourceNameLength )) )
	{
		//
		// If we have a resource's name
		//
		if( ERROR_SUCCESS == dwResultClusterEnum )
		{

			if ( !(hResource = OpenClusterResource( hCluster, lpwszResourceName )) )
			{
				dwError = GetLastError();
				break;
			}

            // If the resource type is "IIS Server Instance", or some other one that is 
            // dependent upon the iis services, then we need to stop it.
            iClusDependsOnIISServices = CheckForIISDependentClusters(hResource);
            if (iClusDependsOnIISServices)
			{
	            CLUSTER_RESOURCE_STATE TheState = GetClusterResourceState(hResource,NULL,0,NULL,0);
                if (TheState == ClusterResourceOnline || TheState == ClusterResourceOnlinePending)
                {
                    HKEY hKey;
                    if ( hKey = GetClusterResourceKey( hResource, KEY_READ ) )
                    {
                        //
                        // Get the resource name.
                        //
                        LPWSTR lpwsResourceName = NULL;
                        lpwsResourceName = GetParameter( hKey, L"Name" );
                        if ( lpwsResourceName != NULL ) 
                        {
                            // this is a resource which we will try to stop
                            // so we should save the name somewhere in like a global list
                            iisDebugOut((LOG_TYPE_TRACE, _T("OfflineClusterResource:'%s'\n"),lpwsResourceName));
                            ListOfClusResources_Add(lpwsResourceName);
                        }
                        if (lpwsResourceName){LocalFree((LPWSTR) lpwsResourceName);}

                        ClusterRegCloseKey(hKey);
                    }

                    //
                    // If the resource doesn't come offline quickly then wait 
                    //
                    if ( ERROR_IO_PENDING == OfflineClusterResource( hResource ) )
                    {
                        for(int iRetry=0; iRetry < MAX_OFFLINE_RETRIES; iRetry++)
                        {
                            Sleep( DELAY_BETWEEN_CALLS_TO_OFFLINE );

                            if ( ERROR_SUCCESS == OfflineClusterResource( hResource ) )
                            {
                                break;
                            }
                        }	
                    }
                }
			CloseClusterResource( hResource );
            }
			
			dwResourceIndex++;
		}
			
		//
		// If the buffer wasn't large enough then retry with a larger buffer
		//
		if( ERROR_MORE_DATA == dwResultClusterEnum )
		{
			//
			// Set the buffer size to the required size reallocate the buffer
			//
			LPWSTR lpwszResourceNameTmp = lpwszResourceName;

			//
			// After returning from ClusterEnum dwClusterEnumResourceNameLength 
			// doesn't include the null terminator character
			//
			dwResourceNameBufferLength = dwClusterEnumResourceNameLength + 1;

			if ( !(lpwszResourceName = 
			      (LPWSTR) LocalReAlloc (lpwszResourceName, dwResourceNameBufferLength * sizeof(WCHAR), 0)) )
			{
				dwError = GetLastError();

				LocalFree( lpwszResourceNameTmp );	
				lpwszResourceNameTmp = NULL;
				break;
			}
		}

		//
		// Reset dwResourceNameLength with the size of the number of characters in the buffer
		// You have to do this because everytime you call ClusterEnum is sets your buffer length 
		// argument to the number of characters in the string it's returning.
		//
		dwClusterEnumResourceNameLength = dwResourceNameBufferLength;
	}	


clean_up:

	if ( lpwszResourceName )
	{
		LocalFree( lpwszResourceName );
		lpwszResourceName = NULL;
	}
	
	if ( hClusResEnum )
	{
		ClusterCloseEnum( hClusResEnum );
		hClusResEnum = NULL;
	}

	if ( hCluster )
	{
		CloseCluster( hCluster );
		hCluster = NULL;
	}

    iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("BringALLIISClusterResourcesOffline:end.ret=0x%x\n"),dwError));
	return dwError;
}


DWORD BringALLIISClusterResourcesOnline()
{
    //
    // The return code
    //
    DWORD dwError = ERROR_SUCCESS;

    //
    // Handle for the cluster
    //
    HCLUSTER hCluster = NULL;

    //
    // Handle for the cluster enumerator
    //
    HCLUSENUM hClusResEnum = NULL;

    //
    // Handle to a resource
    // 
    HRESOURCE hResource = NULL;

    //
    // The index of the resources we're taking offline
    //
    DWORD dwResourceIndex = 0;

    //
    // The type cluster object being enumerated returned by the ClusterEnum function
    //
    DWORD dwObjectType = 0;

    //
    // The name of the cluster resource returned by the ClusterEnum function
    //
    LPWSTR lpwszResourceName = NULL;

    //
    // The return code from the call to ClusterEnum
    //
    DWORD dwResultClusterEnum = ERROR_SUCCESS;

    //
    // The size of the buffer (in characters) that is used to hold the resource name's length
    //	
    DWORD dwResourceNameBufferLength = INITIAL_RESOURCE_NAME_SIZE;

    //
    // Size of the resource name passed to and returned by the ClusterEnum function
    //	
    DWORD dwClusterEnumResourceNameLength = dwResourceNameBufferLength;

    BOOL iClusDependsOnIISServices = FALSE;


    //
    // Open the cluster
    //
    if ( !(hCluster = OpenCluster(NULL)) )
    {
        dwError = GetLastError();
        // This will fail with RPC_S_SERVER_UNAVAILABLE "The RPC server is unavailable" if there is no cluster on this system
        if (hCluster == NULL)
        {
            if ( (dwError != RPC_S_SERVER_UNAVAILABLE) &&
                 (dwError != EPT_S_NOT_REGISTERED ) )
            {
                iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOnline:OpenCluster failed err=0x%x.\n"),dwError));
            }
        }
        else
        {
            iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOnline:OpenCluster failed err=0x%x.\n"),dwError));
        }
        goto clean_up;
    }

    iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("BringALLIISClusterResourcesOnline:end.ret=0x%x\n"),dwError));

    //
    // Get Enumerator for the cluster resouces
    // 
    if ( !(hClusResEnum = ClusterOpenEnum( hCluster, CLUSTER_ENUM_RESOURCE )) )
    {
        dwError = GetLastError();
        iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOnline:ClusterOpenEnum failed err=0x%x.\n"),dwError));
        goto clean_up;	
    }
	
	//
	// Enumerate the Resources in the cluster
	// 
	
    //
    // Allocate memory to hold the cluster resource name as we enumerate the resources
    //
    if ( !(lpwszResourceName = (LPWSTR) LocalAlloc(LPTR, dwResourceNameBufferLength * sizeof(WCHAR))) )
    {
        dwError = GetLastError();
        iisDebugOut((LOG_TYPE_ERROR, _T("BringALLIISClusterResourcesOnline:LocalAlloc failed err=0x%x.\n"),dwError));
        goto clean_up;
    }

    // 
    // Enumerate all of the resources in the cluster and take the IIS Server Instance's offline
    //
    while( ERROR_NO_MORE_ITEMS  != 
        (dwResultClusterEnum = ClusterEnum(hClusResEnum,
            dwResourceIndex, 
            &dwObjectType, 
            lpwszResourceName,
            &dwClusterEnumResourceNameLength )) )
	{
		//
		// If we have a resource's name
		//
		if( ERROR_SUCCESS == dwResultClusterEnum )
		{

			if ( !(hResource = OpenClusterResource( hCluster, lpwszResourceName )) )
			{
				dwError = GetLastError();
				break;
			}

            // If the resource type is "IIS Server Instance", or some other one that is 
            // dependent upon the iis services, then we probably stopped, it.
            iClusDependsOnIISServices = CheckForIISDependentClusters(hResource);
			if (iClusDependsOnIISServices)
			{
	            CLUSTER_RESOURCE_STATE TheState = GetClusterResourceState(hResource,NULL,0,NULL,0);
	            if (TheState == ClusterResourceOffline || TheState == ClusterResourceOfflinePending)
	            {
                    int iRestart = FALSE;
                    LPWSTR lpwsResourceName = NULL;

                    HKEY hKey;
                    if ( hKey = GetClusterResourceKey( hResource, KEY_READ ) )
                    {
                        //
                        // Get the resource name.
                        //
                        lpwsResourceName = GetParameter( hKey, L"Name" );
                        if ( lpwsResourceName != NULL ) 
                        {
                            iRestart = ListOfClusResources_Check(lpwsResourceName);
                        }
                        ClusterRegCloseKey(hKey);
                    }

                    if (TRUE == iRestart)
                    {
                        iisDebugOut((LOG_TYPE_TRACE, _T("OnlineClusterResource:'%s'.\n"),lpwsResourceName));
                        OnlineClusterResource(hResource);
                        if (lpwsResourceName){LocalFree((LPWSTR) lpwsResourceName);}
                    }
			    }
            }
            CloseClusterResource( hResource );
		
			dwResourceIndex++;
		}
			
        //
        // If the buffer wasn't large enough then retry with a larger buffer
        //
        if( ERROR_MORE_DATA == dwResultClusterEnum )
        {
            //
            // Set the buffer size to the required size reallocate the buffer
            //
            LPWSTR lpwszResourceNameTmp = lpwszResourceName;

            //
            // After returning from ClusterEnum dwClusterEnumResourceNameLength 
            // doesn't include the null terminator character
            //
            dwResourceNameBufferLength = dwClusterEnumResourceNameLength + 1;

            if ( !(lpwszResourceName = 
            (LPWSTR) LocalReAlloc (lpwszResourceName, dwResourceNameBufferLength * sizeof(WCHAR), 0)) )
            {
                dwError = GetLastError();

                LocalFree( lpwszResourceNameTmp );	
                lpwszResourceNameTmp = NULL;
                break;
            }
        }

		//
		// Reset dwResourceNameLength with the size of the number of characters in the buffer
		// You have to do this because everytime you call ClusterEnum is sets your buffer length 
		// argument to the number of characters in the string it's returning.
		//
		dwClusterEnumResourceNameLength = dwResourceNameBufferLength;
	}	


clean_up:
    if ( lpwszResourceName )
    {
        LocalFree( lpwszResourceName );
        lpwszResourceName = NULL;
    }

    if ( hClusResEnum )
    {
        ClusterCloseEnum( hClusResEnum );
        hClusResEnum = NULL;
    }

    if ( hCluster )
    {
        CloseCluster( hCluster );
        hCluster = NULL;
    }

    iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("BringALLIISClusterResourcesOnline:end.ret=0x%x\n"),dwError));
    return dwError;
}


LPWSTR GetParameter(
    IN HKEY ClusterKey,
    IN LPCWSTR ValueName
    )
/*++
Routine Description:

    Reads a REG_SZ parameter from the cluster regitry, and allocates the
    necessary storage for it.

Arguments:

    ClusterKey - supplies the cluster key where the parameter is stored.

    ValueName - supplies the name of the value.

Return Value:

    A pointer to a buffer containing the parameter value on success.

    NULL on failure.

--*/
{
    LPWSTR  value = NULL;
    DWORD   valueLength;
    DWORD   valueType;
    DWORD   status;

    valueLength = 0;
    status = ClusterRegQueryValue( ClusterKey,ValueName,&valueType,NULL,&valueLength );
    if ( (status != ERROR_SUCCESS) && (status != ERROR_MORE_DATA) ) 
    {
        SetLastError(status);
        return(NULL);
    }
    if ( valueType == REG_SZ ) 
    {
        valueLength += sizeof(UNICODE_NULL);
    }

    value = (LPWSTR) LocalAlloc(LMEM_FIXED, valueLength);
    if ( value == NULL ) 
        {return(NULL);}
    status = ClusterRegQueryValue(ClusterKey,ValueName,&valueType,(LPBYTE)value,&valueLength);
    if ( status != ERROR_SUCCESS) 
    {
        LocalFree(value);
        SetLastError(status);
        value = NULL;
    }
    return(value);
}


INT CheckForIISDependentClusters(HRESOURCE hResource)
{
    INT iReturn = FALSE;

	// If the resource type is "IIS Server Instance", 
	// "SMTP Server Instance" or "NNTP Server Instance" then take it offline
    iReturn = ResUtilResourceTypesEqual(IIS_RESOURCE_TYPE_NAME, hResource);
    if (!iReturn){iReturn = ResUtilResourceTypesEqual(SMTP_RESOURCE_TYPE_NAME, hResource);}
    if (!iReturn){iReturn = ResUtilResourceTypesEqual(NNTP_RESOURCE_TYPE_NAME, hResource);}

    // check for other ones which might be listed in the inf file!
    if (!iReturn && g_pTheApp->m_hInfHandle)
    {
        CStringList strList;
        CString csTheSection = _T("ClusterResType_DependsonIIS");

        if (GetSectionNameToDo(g_pTheApp->m_hInfHandle, csTheSection))
        {
            if (ERROR_SUCCESS == FillStrListWithListOfSections(g_pTheApp->m_hInfHandle, strList, csTheSection))
            {
                // loop thru the list returned back
                if (strList.IsEmpty() == FALSE)
                {
                    POSITION pos;
                    CString csEntry;

                    pos = strList.GetHeadPosition();
                    while (pos) 
                    {
                        csEntry = strList.GetAt(pos);

                        int iTempReturn = FALSE;
                        iTempReturn = ResUtilResourceTypesEqual(csEntry, hResource);
                        if (iTempReturn)
                        {
                            iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("CheckForIISDependentClusters:yes='%s'\n"),csEntry));
                            iReturn = TRUE;
                            break;
                        }
                        strList.GetNext(pos);
                    }
                }
            }
        }
    }

    return iReturn;
}


DWORD WINAPI DoesThisServiceTypeExistInCluster(PVOID pInfo)
{
    INT iTemp = FALSE;
    CLUSTER_SVC_INFO_FILL_STRUCT * pMyStructOfInfo;
    pMyStructOfInfo = (CLUSTER_SVC_INFO_FILL_STRUCT *) pInfo;

    //pMyStructOfInfo->szTheClusterName
    //pMyStructOfInfo->pszTheServiceType
    //pMyStructOfInfo->csTheReturnServiceResName
    //pMyStructOfInfo->dwReturnStatus

	//
	// The return code
	//
    DWORD dwReturn = ERROR_NOT_FOUND;
    pMyStructOfInfo->dwReturnStatus = dwReturn;

	//
	// Handle for the cluster
	//
	HCLUSTER hCluster = NULL;

	//
	// Handle for the cluster enumerator
	//
	HCLUSENUM hClusResEnum = NULL;

	//
	// Handle to a resource
	// 
	HRESOURCE hResource = NULL;

	//
	// The index of the resources we're taking offline
	//
	DWORD dwResourceIndex = 0;

	//
	// The type cluster object being enumerated returned by the ClusterEnum function
	//
	DWORD dwObjectType = 0;

	//
	// The name of the cluster resource returned by the ClusterEnum function
	//
	LPWSTR lpwszResourceName = NULL;
	
	//
	// The return code from the call to ClusterEnum
	//
	DWORD dwResultClusterEnum = ERROR_SUCCESS;

	//
	// The size of the buffer (in characters) that is used to hold the resource name's length
	//	
	DWORD dwResourceNameBufferLength = INITIAL_RESOURCE_NAME_SIZE;

	//
	// Size of the resource name passed to and returned by the ClusterEnum function
	//	
	DWORD dwClusterEnumResourceNameLength = dwResourceNameBufferLength;

    //
    //  Open the cluster
    //
    hCluster = OpenCluster(pMyStructOfInfo->szTheClusterName);
    if( !hCluster )
    {
        dwReturn = GetLastError();
        goto DoesThisServiceTypeExistInCluster_Exit;	
    }

    //
    // Get Enumerator for the cluster resouces
    // 
    if ( !(hClusResEnum = ClusterOpenEnum( hCluster, CLUSTER_ENUM_RESOURCE )) )
    {
        dwReturn = GetLastError();
        goto DoesThisServiceTypeExistInCluster_Exit;	
    }

    //
    // Enumerate the Resources in the cluster
    // 
	
    //
    // Allocate memory to hold the cluster resource name as we enumerate the resources
    //
    if ( !(lpwszResourceName = (LPWSTR) LocalAlloc(LPTR, dwResourceNameBufferLength * sizeof(WCHAR))) )
    {
        dwReturn = GetLastError();
        goto DoesThisServiceTypeExistInCluster_Exit;
    }

    // 
    // Enumerate all of the resources in the cluster
    //
    while( ERROR_NO_MORE_ITEMS  != (dwResultClusterEnum = ClusterEnum(hClusResEnum,dwResourceIndex,&dwObjectType,lpwszResourceName,&dwClusterEnumResourceNameLength)) )
    {
        //
        // If we have a resource's name
        //
		if( ERROR_SUCCESS == dwResultClusterEnum )
		{

			if ( !(hResource = OpenClusterResource( hCluster, lpwszResourceName )) )
			{
				dwReturn = GetLastError();
				break;
			}

            // If the resource type is "IIS Server Instance", or one that depends upon iis like smtp or nntp then
            // check further to see if they have our services (W3SVC or MSFTPSVC)

            iTemp = ResUtilResourceTypesEqual(IIS_RESOURCE_TYPE_NAME, hResource);
            if (!iTemp){iTemp = ResUtilResourceTypesEqual(SMTP_RESOURCE_TYPE_NAME, hResource);}
            if (!iTemp){iTemp = ResUtilResourceTypesEqual(NNTP_RESOURCE_TYPE_NAME, hResource);}

            if (TRUE == iTemp)
            {
                // if the resource hangs it will hang on this call
                pMyStructOfInfo->dwReturnStatus = ERROR_INVALID_BLOCK;
                if (ERROR_SUCCESS == IsResourceThisTypeOfService(hResource, pMyStructOfInfo->pszTheServiceType))
                {
                    CString csResName;
                    //
                    // Yes! we found it
                    //
                    dwReturn = ERROR_SUCCESS;

                    // Display the resource name for fun
                    if (TRUE == GetClusterResName(hResource, &csResName))
                    {
                        // copy it to the return string
                        *pMyStructOfInfo->csTheReturnServiceResName = csResName;
                    }

                    CloseClusterResource( hResource );
                    goto DoesThisServiceTypeExistInCluster_Exit;
                }
                dwReturn = ERROR_NOT_FOUND;
			    CloseClusterResource( hResource );
            }
			
			dwResourceIndex++;
		}

		//
		// If the buffer wasn't large enough then retry with a larger buffer
		//
		if( ERROR_MORE_DATA == dwResultClusterEnum )
		{
			//
			// Set the buffer size to the required size reallocate the buffer
			//
			LPWSTR lpwszResourceNameTmp = lpwszResourceName;

			//
			// After returning from ClusterEnum dwClusterEnumResourceNameLength 
			// doesn't include the null terminator character
			//
			dwResourceNameBufferLength = dwClusterEnumResourceNameLength + 1;

			if ( !(lpwszResourceName = 
			      (LPWSTR) LocalReAlloc (lpwszResourceName, dwResourceNameBufferLength * sizeof(WCHAR), 0)) )
			{
                dwReturn = GetLastError();

				LocalFree( lpwszResourceNameTmp );	
				lpwszResourceNameTmp = NULL;
				break;
			}
		}

		//
		// Reset dwResourceNameLength with the size of the number of characters in the buffer
		// You have to do this because everytime you call ClusterEnum is sets your buffer length 
		// argument to the number of characters in the string it's returning.
		//
		dwClusterEnumResourceNameLength = dwResourceNameBufferLength;
	}	


DoesThisServiceTypeExistInCluster_Exit:
	if ( lpwszResourceName )
	{
		LocalFree( lpwszResourceName );
		lpwszResourceName = NULL;
	}
	
	if ( hClusResEnum )
	{
		ClusterCloseEnum( hClusResEnum );
		hClusResEnum = NULL;
	}

	if ( hCluster )
	{
		CloseCluster( hCluster );
		hCluster = NULL;
	}

    pMyStructOfInfo->dwReturnStatus = dwReturn;
    return dwReturn;
}


DWORD IsResourceThisTypeOfService(HRESOURCE hResource, LPWSTR pszTheServiceType)
{
    DWORD dwReturn = ERROR_NOT_FOUND;
    HINSTANCE hClusapi  = NULL;
    HINSTANCE hResutils = NULL;

    PFN_CLUSTERRESOURCECONTROL      pfnClusterResourceControl;
    PFN_RESUTILFINDSZPROPERTY       pfnResUtilFindSzProperty;

	//
	// Initial size of the buffer
	//
	DWORD dwBufferSize = 256;
	
	//
	// The requested buffer size, and the number of bytes actually in the returned buffer
	//
	DWORD dwRequestedBufferSize = dwBufferSize;

	//
	// Result from the call to the cluster resource control function
	//
	DWORD dwResult;

	//
	// Buffer that holds the property list for this resource
	//
	LPVOID lpvPropList = NULL;

	//
	// The Proivate property that is being read
	//
	LPWSTR lpwszPrivateProp = NULL;

    //
    // Load cluster dll's
    //
    hClusapi = LoadLibrary( L"clusapi.dll" );
    if (!hClusapi)
        {
        dwReturn = TYPE_E_CANTLOADLIBRARY;
        goto IsResourceThisTypeOfService_Exit;
        }

    pfnClusterResourceControl = (PFN_CLUSTERRESOURCECONTROL)GetProcAddress( hClusapi, "ClusterResourceControl" );
    if (!pfnClusterResourceControl)
        {
        dwReturn = ERROR_PROC_NOT_FOUND;
        goto IsResourceThisTypeOfService_Exit;
        }

    hResutils = LoadLibrary( L"resutils.dll" );
    if (!hResutils)
        {
        dwReturn = TYPE_E_CANTLOADLIBRARY;
        goto IsResourceThisTypeOfService_Exit;
        }
    pfnResUtilFindSzProperty = (PFN_RESUTILFINDSZPROPERTY)GetProcAddress( hResutils, "ResUtilFindSzProperty" );
    if (!pfnResUtilFindSzProperty)
        {
        dwReturn = ERROR_PROC_NOT_FOUND;
        goto IsResourceThisTypeOfService_Exit;
        }

	//
	// Allocate memory for the resource type
	//
	lpvPropList = (LPWSTR) HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, dwBufferSize * sizeof(WCHAR) );
	if( lpvPropList == NULL)
	{
		lpvPropList = NULL;
        dwReturn = E_OUTOFMEMORY;
        goto IsResourceThisTypeOfService_Exit;
	}

	//
	// Allocate memory for the Property
	//
	lpwszPrivateProp = (LPWSTR) HeapAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY, (_MAX_PATH+_MAX_PATH+1) * sizeof(WCHAR) );
	if( lpwszPrivateProp == NULL)
	{
		lpvPropList = NULL;
        dwReturn = E_OUTOFMEMORY;
        goto IsResourceThisTypeOfService_Exit;
	}
	
	//
	// Get the resource's private properties (Service , InstanceId)
	//
	while( 1 )
	{
		dwResult = pfnClusterResourceControl(hResource,NULL,CLUSCTL_RESOURCE_GET_PRIVATE_PROPERTIES,NULL,0,lpvPropList,dwBufferSize,&dwRequestedBufferSize );
		if( ERROR_SUCCESS == dwResult )
		{

            // ---------------------
            // what the entries are:
            // AccessMask (dword) = 5
            // Alias (string)     = "virtual dir name"
            // Directory (string) = "c:\la\lalalala"
            // ServiceName (string) = W3SVC, MSFTPSVC, GOPHERSVC
            // ---------------------

            //
            // Get the "ServiceName" entry
            //
            dwResult = pfnResUtilFindSzProperty( lpvPropList, &dwRequestedBufferSize, L"ServiceName", &lpwszPrivateProp);
			if( dwResult != ERROR_SUCCESS )
			{
                dwReturn = dwResult;
                goto IsResourceThisTypeOfService_Exit;
			}

            if (_wcsicmp(lpwszPrivateProp, pszTheServiceType) == 0)
            {
                // Okay, we found at least 1 service name that matches
                // the one that was passed -- which we're supposed to look for
                // return success
                dwReturn = ERROR_SUCCESS;
            }
            goto IsResourceThisTypeOfService_Exit;
		}

		if( ERROR_MORE_DATA == dwResult )
		{
			//
			// Set the buffer size to the required size reallocate the buffer
			//
			dwBufferSize = ++dwRequestedBufferSize;

			lpvPropList = (LPWSTR) HeapReAlloc (GetProcessHeap(), HEAP_ZERO_MEMORY | HEAP_NO_SERIALIZE, lpvPropList, dwBufferSize * sizeof(WCHAR) );
			if ( lpvPropList == NULL)
			{
                dwReturn = E_OUTOFMEMORY;
                goto IsResourceThisTypeOfService_Exit;
			}
		}
	}

IsResourceThisTypeOfService_Exit:
    if (lpwszPrivateProp)
        {HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, lpwszPrivateProp);}
    if (lpvPropList)
        {HeapFree(GetProcessHeap(), HEAP_NO_SERIALIZE, lpvPropList);}
    if (hClusapi)
        {FreeLibrary(hClusapi);}
    if (hResutils)
        {FreeLibrary(hClusapi);}
   
    return dwReturn;
}


INT GetClusterResName(HRESOURCE hResource, CString * csReturnedName)
{
    int iReturn = FALSE;
    HKEY hKey;
    if ( hKey = GetClusterResourceKey( hResource, KEY_READ ) )
    {
        //
        // Get the resource name.
        //
        LPWSTR lpwsResourceName = NULL;
        lpwsResourceName = GetParameter( hKey, L"Name" );
        if ( lpwsResourceName != NULL ) 
        {
            //wcscpy(csReturnedName,lpwsResourceName);
            *csReturnedName = lpwsResourceName;
            iReturn = TRUE;
        }
        if (lpwsResourceName){LocalFree((LPWSTR) lpwsResourceName);}

        ClusterRegCloseKey(hKey);
    }
    return iReturn;
}




INT DoClusterServiceCheck(CLUSTER_SVC_INFO_FILL_STRUCT * pMyStructOfInfo)
{
    int iReturn = FALSE;
    DWORD ThreadID = 0;
    DWORD status = 0;
   
    HANDLE hMyThread = CreateThread(NULL,0,DoesThisServiceTypeExistInCluster,pMyStructOfInfo,0,&ThreadID);
    if (hMyThread)
    {
        // wait for 30 secs only
        DWORD res = WaitForSingleObject(hMyThread,30*1000);
        if (res == WAIT_TIMEOUT)
        {
            iisDebugOut((LOG_TYPE_TRACE_WIN32_API, _T("ERROR DoClusterServiceCheck thread never finished...\n")));
		    GetExitCodeThread(hMyThread, &status);
		    if (status == STILL_ACTIVE) 
            {
			    if (hMyThread != NULL)
                    {TerminateThread(hMyThread, 0);}
		    }
        }
        else
        {
            GetExitCodeThread(hMyThread, &status);
		    if (status == STILL_ACTIVE) 
            {
			    if (hMyThread != NULL)
                    {TerminateThread(hMyThread, 0);}
		    }
            else
            {
                if (ERROR_SUCCESS == status)
                    {iReturn = TRUE;}
            }

            if (hMyThread != NULL)
                {CloseHandle(hMyThread);}
        }
    }

    return iReturn;
}


INT DoesClusterServiceExist(void)
{
    if (-1 == g_ClusterSVCExist)
    {
        CRegKey regClusSvc(HKEY_LOCAL_MACHINE, scClusterPath2, KEY_READ);
        if ( (HKEY) regClusSvc )
        {
            g_ClusterSVCExist = 1;
        }
        else
        {
            g_ClusterSVCExist = 0;
        }
    }
    return g_ClusterSVCExist;
}
#endif //_CHICAGO_