/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
/*****************************************************************************
*
*   $/Plasma20/Sources/Plasma/NucleusLib/pnIniExe/Private/pnIniSrv.cpp
*   
***/

#include "../Pch.h"
#pragma hdrstop

#ifdef SERVER

/*****************************************************************************
*
*   Internal
*
***/

const unsigned          CLASS_C_SUBNET_MASK     = 0xFFFFFF00;
const NetAddressNode    LOOPBACK_ADDRESS_NODE   = 0x7F000001;


//============================================================================
struct PrivilegedAddressBlock : THashKeyVal<unsigned> {
    HASHLINK(PrivilegedAddressBlock)    link;
    
    NetAddressNode                  startAddress;
    NetAddressNode                  endAddress;
    EServerRights                   serverRights;
};

//============================================================================

#define ADDRESS_BLOCK_TABLE HASHTABLEDECL(PrivilegedAddressBlock, THashKeyVal<unsigned>, link)

static CCritSect            s_critsect;
static ADDRESS_BLOCK_TABLE  s_addressBlocks;


//============================================================================
static void SrvRightsDestroy () {
    s_critsect.Enter();
    {
        s_addressBlocks.Clear();
    }
    s_critsect.Leave();
}

//============================================================================
AUTO_INIT_FUNC(InitSrvRightsIni) {
    atexit(SrvRightsDestroy);
}

//============================================================================
static EServerRights GetServerRightsFromString(const wchar string[]) {
    if (StrCmpI(string, L"Server") == 0)
        return kSrvRightsServer;
    else if (StrCmpI(string, L"Basic") == 0)
        return kSrvRightsBasic;
    else
        return kSrvRightsNone;
}

static void IAddAddressBlock(ADDRESS_BLOCK_TABLE & addrList, NetAddressNode startAddr, NetAddressNode endAddr, EServerRights srvRights) {
    PrivilegedAddressBlock* addrBlock = NEW(PrivilegedAddressBlock);

    addrBlock->startAddress = startAddr;
    addrBlock->serverRights = srvRights;

    if (endAddr == 0)
        addrBlock->endAddress = addrBlock->startAddress;
    else
        addrBlock->endAddress = endAddr;

    if ( (addrBlock->startAddress & CLASS_C_SUBNET_MASK) != (addrBlock->endAddress & CLASS_C_SUBNET_MASK) ) {
        LogMsg(kLogDebug, L"IniSrv: Error creating privileged address block - start address and end address aren't from the same subnet.");
        DEL(addrBlock);
    }
    else {
        addrBlock->SetValue(startAddr & CLASS_C_SUBNET_MASK);
        addrList.Add(addrBlock);
    }
}

/*****************************************************************************
*
*   Exports
*
***/

//============================================================================
EServerRights SrvIniGetServerRightsByNode (NetAddressNode addrNode) {
    EServerRights retVal = kSrvRightsBasic;
    unsigned addrSubNet = (addrNode & CLASS_C_SUBNET_MASK);

    s_critsect.Enter();
    {
        PrivilegedAddressBlock* addrBlock = s_addressBlocks.Find(addrSubNet);
        while (addrBlock) {
            if (addrBlock->startAddress <= addrNode && addrNode <= addrBlock->endAddress) {
                retVal = addrBlock->serverRights;
                break;
            }

            addrBlock = s_addressBlocks.FindNext(addrSubNet, addrBlock);
        }
    }
    s_critsect.Leave();

    return retVal;
}

//============================================================================
EServerRights SrvIniGetServerRights (const NetAddress & addr) {
    NetAddressNode addrNode = NetAddressGetNode(addr);

    return SrvIniGetServerRightsByNode(addrNode);
}

//============================================================================
void SrvIniParseServerRights (Ini * ini) {
    unsigned iter;
    const IniValue *value;
    ADDRESS_BLOCK_TABLE newaddresstable;
    ADDRESS_BLOCK_TABLE removeaddresstable;

    value = IniGetFirstValue(
        ini,
        L"Privileged Addresses",
        L"Addr",
        &iter
    );

    // add ini file address blocks
    while (value) {
        wchar valStr[20];
        NetAddressNode start;
        NetAddressNode end;
        EServerRights rights;

        IniGetString(value, valStr, arrsize(valStr), 0);
        start = NetAddressNodeFromString(valStr, nil);

        IniGetString(value, valStr, arrsize(valStr), 1);
        end = NetAddressNodeFromString(valStr, nil);

        IniGetString(value, valStr, arrsize(valStr), 2);
        rights = GetServerRightsFromString(valStr);

        IAddAddressBlock(newaddresstable, start, end, rights);

        value = IniGetNextValue(value, &iter);
    }

    // Add local addresses and loopback
    NetAddressNode nodes[16];
    unsigned count = NetAddressGetLocal(arrsize(nodes), nodes);

    for (unsigned i = 0; i < count; ++i) {
        IAddAddressBlock(newaddresstable, nodes[i], nodes[i], kSrvRightsServer);
    }
    IAddAddressBlock(newaddresstable, LOOPBACK_ADDRESS_NODE, LOOPBACK_ADDRESS_NODE, kSrvRightsServer);

    s_critsect.Enter();
    {
        while (PrivilegedAddressBlock* addrBlock = s_addressBlocks.Head())
            removeaddresstable.Add(addrBlock);

        while (PrivilegedAddressBlock* addrBlock = newaddresstable.Head())
            s_addressBlocks.Add(addrBlock);
    }
    s_critsect.Leave();

    removeaddresstable.Clear();
}


#endif