//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
/*--
Module Name: Website.CPP
Abstract:    Maps IP addresses to a particular website
--*/


#include "httpd.h"


// 
// iphlpapi helper routines
//


//
// Init/deinitialization of web server
//
void CleanupGlobalMemory(DWORD dwWebsites) 
{
    EnterCriticalSection(&g_CritSect);

#if defined (DEBUG) || defined (_DEBUG)
    DEBUGCHK(g_fState != SERVICE_STATE_ON);
    
    if (!g_pWebsites)
    {
        DEBUGCHK(g_cWebsites == 0);
    }

    DEBUGCHK(dwWebsites <= g_cWebsites);
    if (g_pWebsites)
    {
        DEBUGCHK(g_pVars);
    }

    if (dwWebsites == 0)
    {
        DEBUGCHK(g_cWebsites == 0);
    }
#endif

    CHttpRequest::DeleteAllRequestObjects();

    // We must cleanup any non-default websites first.
    // We use dwWebsites and not g_cWebsites as the index counter in the event 
    // that something went wrong on startup and not all the values are initialized.

    for (DWORD i = 0; i < dwWebsites; i++)
    {
        g_pWebsites[i].DeInitGlobals();
    }

    MyFree(g_pWebsites);
    g_cWebsites = 0;

    if (g_pVars) 
    {
        delete g_pVars;
        g_pVars = NULL;
    }
    LeaveCriticalSection(&g_CritSect);
}


BOOL InitializeGlobals() 
{
    EnterCriticalSection(&g_CritSect);
    BOOL fRet = FALSE;
    CReg webSites;
    DWORD dwWebsites;

    DEBUGCHK (g_fState == SERVICE_STATE_STARTING_UP && !g_pVars && !g_pRequestList);
    g_pRequestList = NULL;

    svsutil_ResetInterfaceMapper();

    g_pVars = new CGlobalVariables(NULL,L"");

    if (NULL == g_pVars || !g_pVars->m_fAcceptConnections) 
    {
        SetWebServerState(SERVICE_STATE_SHUTTING_DOWN);
        CleanupGlobalMemory(0);
        goto done;
    }

    // Filters may make reference to logging global var, so make call to filters 
    // outside constructor.  Filters are global and are not setup per-website, so this
    // is the only time we call this function.
    g_pVars->m_fFilters = InitFilters();  

    webSites.Open(HKEY_LOCAL_MACHINE,RK_WEBSITES);

    if (webSites.IsOK() && (0 != (dwWebsites = webSites.NumSubkeys()))) 
    {
        WCHAR szSiteName[MAX_PATH];
        if (NULL == (g_pWebsites = MyRgAllocNZ(CGlobalVariables,dwWebsites))) 
        {
            CleanupGlobalMemory(0);

            SetWebServerState(SERVICE_STATE_OFF);
            goto done;
        }

        for (DWORD i = 0; i < dwWebsites; i++) 
        {
            webSites.EnumKey(szSiteName,SVSUTIL_ARRLEN(szSiteName));
            CReg subReg(webSites,szSiteName);

            if (! subReg.IsOK())
            {
                break;
            }

            g_pWebsites[i].InitGlobals(&subReg,szSiteName);

            if (! g_pWebsites[i].m_fAcceptConnections)
            {
                break;
            }
            g_cWebsites++;
        }

        if (g_cWebsites != i) 
        {
            CleanupGlobalMemory(i);
            SetWebServerState(SERVICE_STATE_OFF);
            goto done;
        }
    }

    SetWebServerState(SERVICE_STATE_ON);

    fRet = TRUE;
done:
    LeaveCriticalSection(&g_CritSect);
    return fRet;
}

// Called on incoming connection, maps to appropriate website.
CGlobalVariables* MapWebsite(SOCKET sock, PSTR szHostHeader, CVRoots **ppVRootTable) 
{
    CGlobalVariables       *pWebsite = NULL;

    PCSTR szAdapterName;
    DWORD i;

    *ppVRootTable = NULL;

    // Need to hold critical section because HandleNotifyAddrChange() can come in 
    // at any time with an IP address notification change.
    EnterCriticalSection(&g_CritSect);
    svsutil_LockInterfaceMapper();

    szAdapterName = svsutil_GetAdapterNameOfConnection(sock);

    if (g_pVars->IsMapped(szAdapterName,szHostHeader)) 
    {
        pWebsite = g_pVars;
        goto done;
    }

    for (i = 0; i < g_cWebsites; i++) 
    {
        if (g_pWebsites[i].IsMapped(szAdapterName,szHostHeader)) 
        {
            pWebsite = &g_pWebsites[i];
            goto done;
        }
    }
    DEBUGCHK(pWebsite == NULL);

done:
    if (!pWebsite) 
    {
        // If no website was found, potentially use default.
        pWebsite = g_pVars->m_fUseDefaultSite ? g_pVars : NULL;
    }

    if (pWebsite) 
    {
        // Request object keeps a reference to VRoot table because 
        // apps can refresh the Website's table in middle of the request.
        pWebsite->m_pVroots->AddRef();
        *ppVRootTable = pWebsite->m_pVroots;
    }

    svsutil_UnLockInterfaceMapper();
    LeaveCriticalSection(&g_CritSect);
    return pWebsite;
}


