/*++

Copyright (c) 1992-1997  Microsoft Corporation

Module Name:

    registry.c

Abstract:

    Contains routines for manipulating registry parameters.

Environment:

    User Mode - Win32

Revision History:

    10-Feb-1997 DonRyan
        Rewrote to implement SNMPv2 support.

--*/
 
///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Include files                                                             //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

#include "globals.h"
#include "registry.h"
#include "contexts.h"
#include "regions.h"
#include "trapmgrs.h"
#include "snmpmgrs.h"
#include "snmpmgmt.h"


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Private procedures                                                        //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Public procedures                                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

INT
InitRegistryNotifiers(
    )
/*++

Routine Description:

    Setup registry notifiers

Arguments:

    None.

Return Values:

    Returns the number of events that have been registered successfully

--*/
{
    DWORD  nEvents = 0;

    // on first call only create the default notifier
    if (g_hDefaultRegNotifier == NULL)
        g_hDefaultRegNotifier = CreateEvent(NULL, FALSE, FALSE, NULL);

    if (g_hDefaultKey == NULL)
    {
        RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                REG_KEY_SNMP_PARAMETERS,
                0,
                KEY_READ,
                &g_hDefaultKey
                );
    }

    // setup the default registry notifier
    if (g_hDefaultRegNotifier &&
        g_hDefaultKey &&
        RegNotifyChangeKeyValue(
            g_hDefaultKey,
            TRUE,
            REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
            g_hDefaultRegNotifier,
            TRUE
            ) == ERROR_SUCCESS)
    {
        SNMPDBG((SNMP_LOG_TRACE,
                 "SNMP: REG: Default reg notifier initialized successfully.\n"));
        nEvents++;
    }

#ifdef _POLICY
    // on first call only create the policy notifier
    if (g_hPolicyRegNotifier == NULL)
        g_hPolicyRegNotifier = CreateEvent(NULL, FALSE, FALSE, NULL);

    if (g_hPolicyKey == NULL)
    {
        RegOpenKeyEx(
            HKEY_LOCAL_MACHINE,
            REG_POLICY_ROOT,
            0,
            KEY_READ,
            &g_hPolicyKey
            );
    }

    // setup the policy registry notifier
    if (g_hPolicyRegNotifier &&
        g_hPolicyKey &&
        RegNotifyChangeKeyValue(
            g_hPolicyKey,
            TRUE,
            REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
            g_hPolicyRegNotifier,
            TRUE
            ) == ERROR_SUCCESS)
    {
        SNMPDBG((SNMP_LOG_TRACE,
                "SNMP: REG: Policy reg notifier initialized successfully.\n"));
        nEvents++;
    }

    SNMPDBG((SNMP_LOG_TRACE,
            "SNMP: REG: Initialized notifiers ... %d.\n", nEvents));
#endif

    return nEvents;
}

BOOL UnloadRegistryNotifiers();

INT
WaitOnRegNotification(
    )
/*++

Routine Description:

    Blocking call - waits for a notification that one of the registry parameters has change

Arguments:

    None.

Return Values:

    Returns the notifyer index (0 for the termination event, !=0 for parameter change)

--*/
{
    HANDLE hNotifiers[3]; // hack - we now (hardcoded) that we are not going to wait for more than three events.
    DWORD  dwNotifiers = 0;
    DWORD  retCode;

    hNotifiers[dwNotifiers++] = g_hTerminationEvent;
    
    if (g_hDefaultRegNotifier != NULL)
        hNotifiers[dwNotifiers++] = g_hDefaultRegNotifier;

#ifdef _POLICY
    if (g_hPolicyRegNotifier != NULL)
        hNotifiers[dwNotifiers++] = g_hPolicyRegNotifier;
#endif

    SNMPDBG((SNMP_LOG_WARNING,
            "SNMP: REG: Will listen for params changes on %d notifiers.\n",
            dwNotifiers));

    retCode = WaitForMultipleObjects(
                dwNotifiers,
                hNotifiers,
                FALSE,
                INFINITE);

    UnloadRegistryNotifiers();

    return retCode;
}
                       
/*++
    Inplace parser for the string formatted OID.
    It is done in O(n) where n is the length of the string formatted OID (two passes)
--*/
BOOL
ConvStringToOid(
    LPTSTR  pStr,
    AsnObjectIdentifier *pOid)
{
    LPTSTR pDup;
    int    iComp;
    DWORD  dwCompValue;
    enum
    {   DOT,
        DIGIT
    }  state = DIGIT;

    // no need to check for parameters consistency (internal call->correct call :o)

    // check the consistency and determine the number of components
    pOid->idLength = 0;

    if (*pStr == _T('.'))   // skip a possible leading '.'
        pStr++;

    for (pDup = pStr; *pDup != _T('\0'); pDup++)
    {
        switch(state)
        {
        case DOT:
            // note: a trailing dot results in a trailing 0
            if (*pDup == _T('.'))
            {
                pOid->idLength++;
                state = DIGIT;
                break;
            }
            // intentionally missing 'break'
        case DIGIT:
            if (*pDup < _T('0') || *pDup > _T('9'))
                return FALSE;
            state = DOT;
            break;
        }
    }
    // add one to the id length as a trailing dot might not be present
    pOid->idLength++;

    // accept oids with two components at least;
    // alloc memory and check for success;
    if (pOid->idLength < 2 ||
        (pOid->ids = SnmpUtilMemAlloc(pOid->idLength * sizeof(UINT))) == NULL)
        return FALSE;

    // we have now enough buffer and a correct input string. Just convert it to OID
    iComp = 0;
    dwCompValue = 0;
    for (pDup = pStr; *pDup != _T('\0'); pDup++)
    {
        if (*pDup == _T('.'))
        {
            pOid->ids[iComp++] = dwCompValue;
            dwCompValue = 0;
        }
        else
        {
            dwCompValue = dwCompValue * 10 + (*pDup - _T('0'));
        }
    }
    pOid->ids[iComp] = dwCompValue;

    return TRUE;
}

BOOL
LoadScalarParameters(
    )

/*++

Routine Description:

    Reads authentication trap flags key and manager timeout value.

Arguments:

    None.

Return Values:

    Returns true if successful.

--*/

{
    HKEY hKey;
    LONG lStatus;
    DWORD dwIndex;
    DWORD dwNameSize;
    DWORD dwValueSize;
    DWORD dwValueType;
    TCHAR szName[MAX_PATH];
    TCHAR szValue[MAX_PATH];
    LPTSTR pszKey = REG_KEY_SNMP_PARAMETERS;
    BOOL  bChangedSysID = FALSE;

    // default value for the IsnmpNameResolutionRetries counter
    // an address will resist to no more than MGRADDR_DYING (16 by default) 
    // consecutive name resolution failures.
    snmpMgmtBase.AsnIntegerPool[IsnmpNameResolutionRetries].asnValue.number = MGRADDR_DYING;

    // open registry subkey    
    lStatus = RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                pszKey,
                0,
                KEY_READ,
                &hKey
                );

    // validate return code
    if (lStatus == ERROR_SUCCESS) 
    {
        // initialize
        dwIndex = 0;

        // loop until error or end of list
        while (lStatus == ERROR_SUCCESS) 
        {

            // initialize buffer sizes
            dwNameSize  = sizeof(szName) / sizeof(szName[0]); // size in number of TCHARs
            dwValueSize = sizeof(szValue); // size in number of bytes

            // read next value
            lStatus = RegEnumValue(
                        hKey, 
                        dwIndex, 
                        szName, 
                        &dwNameSize,
                        NULL, 
                        &dwValueType, 
                        (LPBYTE)szValue, 
                        &dwValueSize
                        );

            // validate return code
            if (lStatus == ERROR_SUCCESS)
            {

                // validate name of value
                if (!lstrcmpi(szName, REG_VALUE_AUTH_TRAPS))
                {
                    // set the 'EnableAuthenTraps' in the internal management structure
                    mgmtISet(IsnmpEnableAuthenTraps, *((PDWORD)&szValue));
                }
                else if (!lstrcmpi(szName, REG_VALUE_MGRRES_COUNTER))
                {
                    // set the 'NameResolutionRetries' in the internal management structure
                    mgmtISet(IsnmpNameResolutionRetries, *((PDWORD)&szValue));
                }
                            
                // next
                dwIndex++;

            }
        }

        RegCloseKey(hKey);
    } 

    // look into MIB2 subtree ..SNMP\Parameters\RFC1156Agent for sysObjectID parameter
    lStatus = RegOpenKeyEx(
                HKEY_LOCAL_MACHINE,
                REG_KEY_MIB2,
                0,
                KEY_READ,
                &hKey
                );

    // validate return code
    if (lStatus == ERROR_SUCCESS)
    {
        LPTSTR  pszOid = szValue;

        dwValueSize = sizeof(szValue); // size in number of bytes

        // first, get the size of the buffer required for the sysObjectID parameter
        lStatus = RegQueryValueEx(
                    hKey,
                    REG_VALUE_SYS_OBJECTID,
                    0,
                    &dwValueType,
                    (LPBYTE)pszOid,
                    &dwValueSize);

        // the ERROR_MORE_DATA is the only error code we expect at this point
        if (lStatus == ERROR_MORE_DATA)
        {
            pszOid = SnmpUtilMemAlloc(dwValueSize);

            // if a buffer was set up correctly, go an read the oid value
            if (pszOid != NULL)
            {
                lStatus = RegQueryValueEx(
                            hKey,
                            REG_VALUE_SYS_OBJECTID,
                            0,
                            &dwValueType,
                            (LPBYTE)pszOid,
                            &dwValueSize);
            }
        }

        // at this point we should succeed
        if (lStatus == ERROR_SUCCESS)
        {
            AsnObjectIdentifier sysObjectID;
            // we have the string representation of the oid, convert it now to an AsnObjectIdentifier

            // implement the convertion here, as I don't want to make this a public function in SNMPAPI.DLL
            // otherwise I'll be forced to handle a lot of useless limit cases..
            if (dwValueType == REG_SZ &&
                ConvStringToOid(pszOid, &sysObjectID))
            {
                // don't free what has been alocated in ConvStringToOid as the buffer will be passed
                // to the management variable below.
                bChangedSysID = (mgmtOSet(OsnmpSysObjectID, &sysObjectID, FALSE) == ERROR_SUCCESS);
            }
            else
            {
                SNMPDBG((SNMP_LOG_WARNING,
                         "SNMP: SVC: LoadScalarParameters() - invalid type or value for sysObjectID param.\n"));

                ReportSnmpEvent(
                    SNMP_EVENT_INVALID_ENTERPRISEOID,
                    0,
                    NULL,
                    0);
            }
        }

        // cleanup the buffer if it was dynamically allocated
        if (pszOid != szValue)
            SnmpUtilMemFree(pszOid);

        // cleanup the registry key
        RegCloseKey(hKey);
    }

    if (!bChangedSysID)
    {
        mgmtOSet(OsnmpSysObjectID, SnmpSvcGetEnterpriseOID(), TRUE);
    }
    // all parameters here have default values, so there is no reason for this function to fail
    // if a parameter could not be found into the registry, its default value will be considered.
    return TRUE;
}


///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Public procedures                                                         //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////

BOOL
LoadRegistryParameters(
    )

/*++

Routine Description:

    Loads registry parameters.

Arguments:

    None.

Return Values:

    Returns true if successful.

--*/

{
    // first thing to do is to setup the registry notifiers. If we don't do this before reading
    // the registry values we might not sense an initial change of the registry.
    InitRegistryNotifiers();

    // need to load first the scalar parameters especially to know how
    // to handle further the name resolution
    LoadScalarParameters();

    // load managers
    LoadPermittedManagers(TRUE);

    // load trap destinations
    LoadTrapDestinations(TRUE);

    // load communities with dynamic update
    LoadValidCommunities(TRUE);

    // load subagents
    LoadSubagents();

    // determine regions
    LoadSupportedRegions();

    return TRUE;
}

BOOL
UnloadRegistryNotifiers(
    )
/*++

Routine Description:

    Unloads registry notifiers

Arguments:

    None.

Return Values:

    Returns TRUE

--*/
{
    if (g_hDefaultRegNotifier != NULL)
    {
        CloseHandle(g_hDefaultRegNotifier);
        g_hDefaultRegNotifier = NULL;
    }
#ifdef _POLICY
    if (g_hPolicyRegNotifier != NULL)
    {
        CloseHandle(g_hPolicyRegNotifier);
        g_hPolicyRegNotifier = NULL;
    }
#endif

    if (g_hDefaultKey != NULL)
    {
        RegCloseKey(g_hDefaultKey);
        g_hDefaultKey = NULL;
    }
#ifdef _POLICY
    if (g_hPolicyKey != NULL)
    {
        RegCloseKey(g_hPolicyKey);
        g_hPolicyKey = NULL;
    }
#endif

    return TRUE;
}


BOOL
UnloadRegistryParameters(
    )

/*++

Routine Description:

    Unloads registry parameters.

Arguments:

    None.

Return Values:

    Returns true if successful.

--*/

{
    // unload the registry notifiers as the first thing to do
    UnloadRegistryNotifiers();

    // unload managers
    UnloadPermittedManagers();

    // unload trap destinations
    UnloadTrapDestinations();

    // unload communities
    UnloadValidCommunities();

    // unload subagents
    UnloadSubagents();

    // unload mib regions
    UnloadSupportedRegions();

    return TRUE;
}

