/*++

Copyright (c) 1992-1996  Microsoft Corporation

Module Name:

    iisutil.c

Abstract:

    IIS Resource utility routine DLL

Author:

    Pete Benoit (v-pbenoi) 12-SEP-1996

Revision History:

--*/

#include    "iisutil.h"

DWORD IISService[] = {
      INET_HTTP,
      INET_FTP,
      INET_GOPHER
      };

#define  IIS_MNGT_DLLNAME  L"infoadmn.dll"

//
// Define some globals used to get iis management routines
//
HINSTANCE iisDLL = NULL;
typedef   NET_API_STATUS (NET_API_FUNCTION *INETINFOGETPROC)(LPWSTR,DWORD,LPINET_INFO_CONFIG_INFO *);
typedef   NET_API_STATUS (NET_API_FUNCTION *INETINFOSETPROC)(LPWSTR,DWORD,LPINET_INFO_CONFIG_INFO);
typedef   NET_API_STATUS (NET_API_FUNCTION *INETINFOFLUSHPROC)(LPWSTR,DWORD);

INETINFOGETPROC   InetInfoGet = NULL;
INETINFOSETPROC   InetInfoSet = NULL;
INETINFOFLUSHPROC InetInfoFlushMemory = NULL;


DWORD
IsIISMngtDllLoaded(
        )
/*++

Routine Description:
    Checks to see if the IIS mngt dll loaded
Arguments:


Return Value:
    ERROR_SUCCESS - Successfully loaded and found
    A Win32 error code on failure.

--*/

{
    if (iisDLL != NULL) {
        return(ERROR_SUCCESS);
    }
    return(ERROR_SERVICE_NOT_ACTIVE);
}

DWORD
IISLoadMngtDll(
        )
/*++

Routine Description:
    This routine tries to load the iis management dll. Then it attempts to
    find the procedures required to manage it.

Arguments:


Return Value:
    ERROR_SUCCESS - Successfully loaded and found
    A Win32 error code on failure.

--*/

{
    DWORD status;
    //
    // Try and load the IIS Mngt dll
    //
    iisDLL = LoadLibrary( IIS_MNGT_DLLNAME );
    if (iisDLL == NULL) {
        return(GetLastError());
    }
    //
    // Try to locate the management routines
    //
    InetInfoGet = (INETINFOGETPROC)GetProcAddress(iisDLL,"InetInfoGetAdminInformation");
    if (InetInfoGet == NULL) {
        status = GetLastError();
        goto error_exit;
    }

    InetInfoSet = (INETINFOSETPROC)GetProcAddress(iisDLL,"InetInfoSetAdminInformation");
    if (InetInfoGet == NULL) {
        status = GetLastError();
        goto error_exit;
    }

    InetInfoFlushMemory = (INETINFOFLUSHPROC)GetProcAddress(iisDLL,"InetInfoFlushMemoryCache");
    if (InetInfoFlushMemory == NULL) {
        status = GetLastError();
        goto error_exit;
    }
    return(ERROR_SUCCESS);
error_exit:
    IISUnloadMngtDll();
    return(status);

} // END IsIISInstalled

VOID
IISUnloadMngtDll(
    )

/*++

Routine Description:
    This routine frees the IIS management DLL

Arguments:


Return Value:

--*/
{
    if (iisDLL != NULL) {
        FreeLibrary( iisDLL);
    }

    iisDLL = NULL;
    InetInfoGet = NULL;
    InetInfoSet = NULL;
    InetInfoFlushMemory = NULL;
}


DWORD
GetIISInfo(
        IN  DWORD                           ServiceType,
        OUT LPINET_INFO_CONFIG_INFO         *IISInfo
        )


/*++

Routine Description:

    Get IIS information. This call returns a pointer
    to an INET_INFO_CONFIG_INFO structure

Arguments:

    ServiceType - The type of service

    IISInfo - return a pointer to an INET_INFO_CONFIG_INFO structure. This
              structure contains the virtual root information


Return Value:

    NET_API_STATUS

--*/

{
     DWORD  Status;
     if (InetInfoGet == NULL) {
         return(ERROR_SERVICE_NOT_ACTIVE);
     }
     Status=InetInfoGet(NULL,IISService[ServiceType],IISInfo);
     return(Status);

} // END GetIISInfo




DWORD
SetIISInfo(
        IN DWORD                           ServiceType,
        IN LPINET_INFO_CONFIG_INFO         IISInfo
        )

/*++

Routine Description:

    Set IIS information. This call sets inet config infor using the
    INET_INFO_CONFIG_INFO structure

Arguments:

    ServiceType - The type of service

    IISInfo - pointer to an INET_INFO_CONFIG_INFO structure. This structure
              contains the virtual root information


Return Value:

    ERROR_SUCCESS
    NET ERROR Status

--*/

{
     DWORD  Status;

     if ((InetInfoSet == NULL) || (InetInfoFlushMemory == NULL)) {
         return(ERROR_SERVICE_NOT_ACTIVE);
     }
     //
     // Only set the VirtualRoots
     //
     IISInfo->FieldControl =  FC_INET_INFO_VIRTUAL_ROOTS;

     Status=InetInfoSet(NULL,IISService[ServiceType],IISInfo);

     if (Status != ERROR_SUCCESS) {
        return(Status);
     }

     //
     // If we sucessfully set the virtual root information
     // flush the memory cache
     //
     Status = InetInfoFlushMemory(NULL,IISService[ServiceType]);

     return(Status);

} // END SetIISInfo




DWORD
OffLineVirtualRoot(
        IN LPIIS_RESOURCE   ResourceEntry,
        IN PLOG_EVENT_ROUTINE   LogEvent
        )

/*++

Routine Description:

    Take offline all Virtual Roots for this resource

Arguments:

    ResourceEntry - The resource that contains a list of virtual roots

Return Value:

    ERROR_SUCCESS
    W32 error code

--*/

{

        LPINET_INFO_CONFIG_INFO         IISInfo         = NULL;
        LPINET_INFO_VIRTUAL_ROOT_LIST   tmpVRL          = NULL;
        LPINET_INFO_VIRTUAL_ROOT_LIST   iisVRL          = NULL;
        LPINET_INFO_VIRTUAL_ROOT_ENTRY  resVR           = NULL;
        DWORD                           Status          = ERROR_SUCCESS;
        DWORD                           i,c;
        BOOLEAN                         MatchFound;
        DWORD                           MatchingEntries = 0;

    //
    // Call IIS Management API to GET list of  Virtual Roots
    //
       Status = GetIISInfo(ResourceEntry->ServiceType, &IISInfo);
       if (Status != ERROR_SUCCESS) {
           goto error_exit;
       }

    //
    // For Robustness check to see if the IIS returned SUCCESS but
    // did not return a valid address
    //
       if (IISInfo == NULL) {
           (LogEvent)(
               ResourceEntry->ResourceHandle,
               LOG_ERROR,
               L"Error [OffLineVirtualRoots] Get IIS information returned NULL\n");
               Status = ERROR_RESOURCE_NOT_FOUND;
           goto error_exit;
       }

    //
    // Save pointer to original VirtualRoot Structure
    //
       iisVRL = IISInfo->VirtualRoots;
       resVR  = ResourceEntry->VirtualRoot;

    //
    // If the caller called terminate after open BUT before online
    // the VR field of the resource could be NULL. Return so we don't accvio
    //

       if (resVR == NULL) {
           (LogEvent)(
               ResourceEntry->ResourceHandle,
               LOG_ERROR,
               L"Error [OffLineVirtualRoots] Resource VR information is NULL\n");
            Status = ERROR_RESOURCE_NOT_FOUND;
            goto error_exit;
       }
    //
    // This is a sanity check
    //
       if ( (resVR->pszRoot == NULL) ||
            (resVR->pszAddress == NULL) ||
            (resVR->pszDirectory == NULL) ) {
           (LogEvent)(
               ResourceEntry->ResourceHandle,
               LOG_ERROR,
               L"Error [OffLineVirtualRoots] Resource has NULL entries for root , addr or directory\n");
           Status = ERROR_RESOURCE_NOT_FOUND;
           goto error_exit;
       }
    //
    // Allocate storage to Filter out Virtual Roots managed by the Cluster
    //
       tmpVRL = LocalAlloc(LPTR,
                           ( (iisVRL->cEntries + 1) *
                           sizeof(INET_INFO_VIRTUAL_ROOT_ENTRY) ) +
                           sizeof(INET_INFO_VIRTUAL_ROOT_LIST));
    //
    // Check to see we got a valid address
    //

       if (tmpVRL == NULL) {
           (LogEvent)(
              ResourceEntry->ResourceHandle,
              LOG_ERROR,
              L"Error [OffLineVirtualRoots] Allocation of TMP VR failed\n");
           Status = ERROR_RESOURCE_NOT_FOUND;
           goto error_exit;
       }

    //
    // This is so ugly ...
    // 1. Enumerate and compare each IIS VR KEY with the resource VR
    // 2. Add ones that don't match to the tmpVR
    // 3. Use the tmpVR to update the IIS service and mask resources entries

       tmpVRL->cEntries = 0;
       for (i=0;i<iisVRL->cEntries ;i++ ) {
           MatchFound = FALSE;


           if ( (_wcsicmp(iisVRL->aVirtRootEntry[i].pszRoot,resVR->pszRoot)           == 0) &&
                (_wcsicmp(iisVRL->aVirtRootEntry[i].pszAddress,resVR->pszAddress)     == 0)
//                (_wcsicmp(iisVRL->aVirtRootEntry[i].pszDirectory,resVR->pszDirectory) == 0)
              ){
        // if all the VR primary keys match then skip this entry
               MatchFound       = TRUE;
               MatchingEntries  +=1;
           } // END if


           // No matching entry found in the Cluster config data so this VR is ok
           if (!MatchFound) {
               tmpVRL->aVirtRootEntry[tmpVRL->cEntries++] = iisVRL->aVirtRootEntry[i];
           } // END if !MatchFound

       } // END for i=0


    IISInfo->VirtualRoots = tmpVRL;
    //
    // Call IIS Management API to SET list of Virtual Roots
    // But only call this if we have something to remove
    //
    if (MatchingEntries > 0) {
        Status = SetIISInfo(ResourceEntry->ServiceType, IISInfo);
    }

    // Destruct any temporary storage
    IISInfo->VirtualRoots   = iisVRL;

error_exit:
    if (IISInfo != NULL) MIDL_user_free((LPVOID)IISInfo);
    if (tmpVRL != NULL)  LocalFree(tmpVRL);

    return(Status);
} //OfflineVirtualRoot



DWORD
OnLineVirtualRoot(
        IN LPIIS_RESOURCE   ResourceEntry,
        IN PLOG_EVENT_ROUTINE   LogEvent
        )

/*++

Routine Description:

    Add this resources Virtual Root to the IIS

Arguments:

    ResourceEntry - The resource that contains a list of virtual roots

Return Value:

    ERROR_SUCCESS
    W32 error code

--*/
{


    LPINET_INFO_CONFIG_INFO         IISInfo     = NULL;
    LPINET_INFO_VIRTUAL_ROOT_LIST   tmpVRL      = NULL;
    LPINET_INFO_VIRTUAL_ROOT_LIST   iisVRL      = NULL;
    LPINET_INFO_VIRTUAL_ROOT_ENTRY  resVR       = NULL;
    DWORD                           Status      = ERROR_SUCCESS;
    DWORD                           i,c;
    BOOLEAN                         MatchFound;


    // Call IIS Management API to GET list of  Virtual Roots
    Status = GetIISInfo(ResourceEntry->ServiceType, &IISInfo);
    if (Status != ERROR_SUCCESS) {
        (LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Error [OnLineVirtualRoots] Get IIS information call failed status =  %1!u! \n",
            Status);
         goto error_exit;
    }

    // Save pointer to original VirtualRoot Structure
    iisVRL = IISInfo->VirtualRoots;
    resVR  = ResourceEntry->VirtualRoot;

    // Add Virtual Roots managed by the resource
    if (resVR == NULL) {
        (LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"Error [OnLineVirtualRoots] NULL virtual root entry \n");
        Status = ERROR_RESOURCE_NOT_FOUND;
        goto error_exit;
    }

    //
    // See if the resource is already on line. In which
    // case this is a duplicate
    //
    if ( VerifyIISService( ResourceEntry,FALSE,LogEvent ) ) {
        // We found a duplicate or this resource is already online
        (LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_INFORMATION,
            L"Warning [OnLineThread] Resource already online or is a duplicate. Check for Unique Alias property\n");
//        Status = ERROR_DUP_NAME;
          Status = ERROR_SUCCESS;
        goto error_exit;
    }

#if DBG
    (LogEvent)(
        ResourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"[OnLineVirtualRoots] about to set info Root = %1!ws! ip = %2!ws! Dir = %3!ws! Mask = %4!u! Name = %5!ws! Pass = %6!ws! \n",
        resVR->pszRoot,
        resVR->pszAddress,
        resVR->pszDirectory,
        resVR->dwMask,
        resVR->pszAccountName,
        resVR->AccountPassword);
#endif
    // Allocate temporary storage for cluster and iis vr entries
    tmpVRL = LocalAlloc(LPTR,
                        ((iisVRL->cEntries + 1) *
                        sizeof(INET_INFO_VIRTUAL_ROOT_ENTRY) ) +
                        sizeof(INET_INFO_VIRTUAL_ROOT_LIST));
    // Make sure we didn't fail on the allocate
    if (tmpVRL == NULL){
        (LogEvent)(
           ResourceEntry->ResourceHandle,
           LOG_ERROR,
           L"[OnLineVirtualRoots] LocalAlloc for temp VRL failed\n");
         Status = ERROR_RESOURCE_NOT_FOUND;
         goto error_exit;
    }


    tmpVRL->aVirtRootEntry[0] = *resVR;


    // Add any additional VR not managed by the cluster
    for (i=0;i<iisVRL->cEntries ;i++ ) {
        tmpVRL->aVirtRootEntry[i+1] = iisVRL->aVirtRootEntry[i];
    }

    tmpVRL->cEntries = iisVRL->cEntries + 1;


    IISInfo->VirtualRoots = tmpVRL;
#if DBG
    for (i=0;i < tmpVRL->cEntries ;i++ ) {
    (LogEvent)(
        ResourceEntry->ResourceHandle,
        LOG_INFORMATION,
        L"[OnLineVirtualRoots] about to set info Root = %1!ws! Ipaddr = %2!ws! Dir = %3!ws! Mask = %4!u! Name = %5!ws! Pass = %6!ws! \n",
        tmpVRL->aVirtRootEntry[i].pszRoot,
        tmpVRL->aVirtRootEntry[i].pszAddress,
        tmpVRL->aVirtRootEntry[i].pszDirectory,
        tmpVRL->aVirtRootEntry[i].dwMask,
        tmpVRL->aVirtRootEntry[i].pszAccountName,
        tmpVRL->aVirtRootEntry[i].AccountPassword);
    }
#endif
    // Call IIS Management API to SET list of Virtual Roots
    Status = SetIISInfo(ResourceEntry->ServiceType, IISInfo);
    if (Status != ERROR_SUCCESS) {
        (LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"[OnLineVirtualRoots] set info status = %1!u!\n",
            Status);
     } else {

        //
        // See if root came on line sucessfully
        //
        if ( !VerifyIISService( ResourceEntry,FALSE,LogEvent ) ) {
            //
            // The root was sucessfully added to iis but the iis could
            // not access the root. Remove it here so the inet manager does
            // not contain bad roots
            //
            OffLineVirtualRoot( ResourceEntry, LogEvent);

            (LogEvent)(
                ResourceEntry->ResourceHandle,
                LOG_ERROR,
                L"ERROR [OnLineThread] IIS could not bring resource online. Verify IIS root is accessable. \n");
            Status = ERROR_RESOURCE_NOT_AVAILABLE;

        } // if !VerifyIISService

    } // if status != ERROR_SUCCESS

    // Destruct any temporary storage
    IISInfo->VirtualRoots   = iisVRL;
error_exit:
    if (IISInfo != NULL) MIDL_user_free((LPVOID)IISInfo);
    if (tmpVRL != NULL)  LocalFree(tmpVRL);

    return(Status);

}  // END OnLineVirtualRoot



VOID
FreeIISResource(
    IN LPIIS_RESOURCE   ResourceEntry
    )

/*++

Routine Description:

    Free all the storage for a IIS_RESOURCE

Arguments:

    vr - virtual root to free

Return Value:

    NONE

--*/

{
    if (ResourceEntry != NULL) {

        if (ResourceEntry->ParametersKey != NULL ) {
            ClusterRegCloseKey( ResourceEntry->ParametersKey );
        }

        if (ResourceEntry->hResource != NULL ) {
            CloseClusterResource( ResourceEntry->hResource );
        }
        LocalFree( ResourceEntry->Params.ServiceName );
        LocalFree( ResourceEntry->Params.Alias );
        LocalFree( ResourceEntry->Params.Directory );

        if (ResourceEntry->VirtualRoot != NULL) {
            DestructVR(ResourceEntry->VirtualRoot);
        }
    } // ResourceEntry != NULL

} // FreeIISResource




VOID
DestructIISResource(
    IN LPIIS_RESOURCE   ResourceEntry
    )

/*++

Routine Description:

    Free all the storage for a ResourceEntry and the ResourceEntry

Arguments:

    vr - virtual root to free

Return Value:

    NONE

--*/
{
    if (ResourceEntry != NULL) {
        FreeIISResource(ResourceEntry),
        LocalFree(ResourceEntry);
    } // ResourceEntry != NULL

} // DestructIISResource




VOID
FreeVR(
      IN  LPINET_INFO_VIRTUAL_ROOT_ENTRY vr
      )

/*++

Routine Description:

    Free all the storage for a Virtual Root

Arguments:

    vr - virtual root to free

Return Value:

    NONE

--*/


{

    if (vr == NULL) {
        return;
    }
    if (vr->pszRoot != NULL) {
        LocalFree(vr->pszRoot);
    }
    if (vr->pszAddress != NULL) {
        LocalFree(vr->pszAddress);
    }
    if (vr->pszDirectory != NULL) {
        LocalFree(vr->pszDirectory);
    }
    if (vr->pszAccountName != NULL) {
        LocalFree(vr->pszAccountName);
    }

}// FreeVR


VOID
DestructVR(
        LPINET_INFO_VIRTUAL_ROOT_ENTRY vr
        )

/*++

Routine Description:

    Free all the storage for a Virtual Root and vr

Arguments:

    vr - virtual root to free

Return Value:

    NONE

--*/


{

    if (vr == NULL) {
        return;
    }
    FreeVR(vr);
    LocalFree(vr);

}// DestructVR



BOOL
VerifyIISService(
    IN LPIIS_RESOURCE       ResourceEntry,
    IN BOOL                 IsAliveFlag,
    IN PLOG_EVENT_ROUTINE   LogEvent
    )

/*++

Routine Description:

    Verify that the IIS service is running and that it has virtual roots
    contained in the resource
    Steps:
       1.  Make sure the IIS service is running by calling the mngt API
       2.  Verfify that the resources virtual roots are currently in
           the running system
       3.  Check the dwError field to make sure the VR for the resource is accessable
       4.  Sanity check to make sure the resources virtual root was found

Arguments:

    Resource - supplies the resource id

    IsAliveFlag - says this is an IsAlive call - used only for debug print

Return Value:

    TRUE - if service is running and service contains resources virtual roots

    FALSE - service is in any other state

--*/
{
    DWORD                           status;
    DWORD                           MatchingEntries = 0;
    DWORD                           VRAccessErrors  = 0;
    BOOL                            MatchFound;
    LPINET_INFO_CONFIG_INFO         IISInfo         = NULL;
    LPINET_INFO_VIRTUAL_ROOT_ENTRY  resVR           = NULL;
    LPINET_INFO_VIRTUAL_ROOT_LIST   iisVRL          = NULL;
    DWORD                           c;
    BOOL                            VerifyStatus    = TRUE;

    //
    // Get IIS virtual root information
    //
    status = GetIISInfo(ResourceEntry->ServiceType, &IISInfo);

    //
    // Check the error status to see if the service is starting
    //
    if (status != ERROR_SUCCESS) {
        if (status == ERROR_SERVICE_NOT_ACTIVE) {
            //
            // Service is not active
            //
            (LogEvent)(
                ResourceEntry->ResourceHandle,
                LOG_ERROR,
                L"IsAlive/LooksAlive ERROR Service NOT active service %1!ws!.\n",
                ResourceEntry->Params.ServiceName );
        } else {
            //
            // Some type of error
            //
            (LogEvent)(
                ResourceEntry->ResourceHandle,
                LOG_ERROR,
                L"IsAlive/LooksAlive ERROR getting information for service %1!ws! status = %2!u!\n",
                ResourceEntry->Params.ServiceName,
                status);
        }
        //
        // Return false;
        //
        VerifyStatus = FALSE;
        goto error_exit;
    }

    iisVRL  = IISInfo->VirtualRoots;
    resVR   = ResourceEntry->VirtualRoot;
    //
    // Now check to see if the Virtual roots for this resource exist in the service
    //
    MatchFound = FALSE;
    for (c=0;c<iisVRL->cEntries ;c++ ) {
        if ( (_wcsicmp(resVR->pszRoot,iisVRL->aVirtRootEntry[c].pszRoot)           == 0) &&
             (_wcsicmp(resVR->pszAddress,iisVRL->aVirtRootEntry[c].pszAddress)     == 0)
///             (_wcsicmp(resVR->pszDirectory,iisVRL->aVirtRootEntry[c].pszDirectory) == 0)
           ){
        //
        // if all the VR primary keys match
        //
            MatchFound       = TRUE;
            MatchingEntries  +=1;
        //
        // See if the IIS can sucessfully access this virtual root
        //
            if (iisVRL->aVirtRootEntry[c].dwError != ERROR_SUCCESS) {
                VRAccessErrors +=1;
                (LogEvent)(
                    ResourceEntry->ResourceHandle,
                    LOG_INFORMATION,
                    L"IsAlive/LooksAlive virtual root %1!ws! IIS Access Error service %2!ws! error = %3!u!\n",
                    resVR->pszRoot,
                    ResourceEntry->Params.ServiceName,
                    iisVRL->aVirtRootEntry[c].dwError);

             } // END dwERROR != ERROR_SUCCESS

             break;

        } // END if

    } // END for c=0

    // No matching entry found in the Cluster config data so this VR is ok
    if (!MatchFound) {
        if (IsAliveFlag) {
            (LogEvent)(
                ResourceEntry->ResourceHandle,
                LOG_ERROR,
                L"ERROR IsAlive/LooksAlive virtual root %1!ws! not found for service %2!ws!\n",
                resVR->pszRoot,
                ResourceEntry->Params.ServiceName,
                status);
        }
        VerifyStatus = FALSE;
        goto error_exit;
    } // END if !MatchFound


    //
    // Perform  sanity check
    //
    if (MatchingEntries != 1) {
        (LogEvent)(
            ResourceEntry->ResourceHandle,
            LOG_ERROR,
            L"ERROR IsAlive/LooksAlive more than one resource is active, service %1!ws!\n",
            ResourceEntry->Params.ServiceName);
        VerifyStatus = FALSE;
        goto error_exit;
    }


    //
    // If the resources virtual root is inaccessable then the resource is offline
    //
    if (VRAccessErrors != 0) {
        VerifyStatus = FALSE;
    }

error_exit:
    if (IISInfo != NULL) MIDL_user_free((LPVOID)IISInfo);
    return(VerifyStatus);

} // VerifyIISService