//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File:        config.cpp
//
// Contents:    CConfigStorage implements read/write to CA configuration data 
//              currently stored under HKLM\System\CCS\Services\Certsvc\
//              Configuration
//
//---------------------------------------------------------------------------

#include <pch.cpp>

#pragma hdrstop

#include <config.h>

#define __dwFILE__	__dwFILE_CERTLIB_CNFGSTG_CPP__


using namespace CertSrv;

HRESULT CConfigStorage::InitMachine(LPCWSTR pcwszMachine)
{
    m_pwszMachine = new WCHAR[wcslen(pcwszMachine)+3];
    if(!m_pwszMachine)
    {
        return E_OUTOFMEMORY;
    }

    m_pwszMachine[0] = L'\0';

    if(pcwszMachine[0]!=L'\\' &&
       pcwszMachine[1]!=L'\\')
    {
        wcscpy(m_pwszMachine, L"\\\\");
    }
    wcscat(m_pwszMachine, pcwszMachine);
    return S_OK;
}

CConfigStorage::~CConfigStorage()
{
    if(m_hRemoteHKLM)
        RegCloseKey(m_hRemoteHKLM);
    if(m_hRootConfigKey)
        RegCloseKey(m_hRootConfigKey);
    if(m_hCAKey)
        RegCloseKey(m_hCAKey);
    if(m_pwszMachine)
        delete[] m_pwszMachine;
}


// Retrieve a CA configuration value. If no authority name is specified, the
// node path must be NULL and value is queried from the Configuration root.
// If an authority name is passed in, the value is retrieved from the authority
// node; if a node path is passed in, it is used relative to the authority node
// to read the value.

// For example, to read Configuration\DBDirectory, call:
// 
//      GetEntry(NULL, NULL, L"DBDirectory", &var)
//
// To read Configuration\MyCA\CAServerName, call:
//
//      GetEntry(L"MyCA", NULL, L"CAServerName", &var)
//
// To read Configuration\MyCA\CSP\HashAlgorithm, call:
//
//      GetEntry(L"MyCA", L"CSP", L"HashAlgorithm"
//
//
// If pcwszValue is null, getentry returns a VT_ARRAY|VT_BSTR with a list
// of subkey names.

HRESULT CConfigStorage::GetEntry(
    LPCWSTR pcwszAuthorityName,
    LPCWSTR pcwszRelativeNodePath,
    LPCWSTR pcwszValue,
    VARIANT *pVariant)
{
    HRESULT hr = S_OK;
    HKEY hKey = NULL;
    LPBYTE pData = NULL, pTmp;
    DWORD cData = 0;
    HKEY hKeyTmp = NULL;
    DWORD dwType;
    DWORD nIndex;
    DWORD cName;
    DWORD cKeys;

    if(EmptyString(pcwszAuthorityName))
    {
        if(!EmptyString(pcwszRelativeNodePath))
        {
            hr = E_INVALIDARG;
            _JumpError(hr, error, "CConfigStorage::GetEntry");
        }

        hr = InitRootKey();
        _JumpIfError(hr, error, "CConfigStorage::InitRootKey");

        hKey = m_hRootConfigKey;
    }
    else
    {
        hr = InitCAKey(pcwszAuthorityName);
        _JumpIfError(hr, error, "CConfigStorage::InitCAKey");

        hKey = m_hCAKey;
    }
    
    CSASSERT(hKey);

    if(!EmptyString(pcwszRelativeNodePath))
    {
        hr = RegOpenKeyEx(
               hKey,
               pcwszRelativeNodePath,
               0,
               KEY_ALL_ACCESS,
               &hKeyTmp);
        if ((HRESULT) ERROR_ACCESS_DENIED == hr)
        {
            hr = RegOpenKeyEx(
                   hKey,
                   pcwszRelativeNodePath,
                   0,
                   KEY_READ,
                   &hKeyTmp);
        }
        _JumpIfErrorStr(hr, error, "RegOpenKeyEx", pcwszRelativeNodePath);
        hKey = hKeyTmp;
    }

    if(EmptyString(pcwszValue))
    {
        dwType = REG_MULTI_SZ;
        cData = 2;

        hr = RegQueryInfoKey(
                hKey,
                NULL,NULL,NULL,
                &cKeys,
                &cName,
                NULL,NULL,NULL,NULL,NULL,NULL);
        _JumpIfError(hr, error, "RegQueryInfoKey");

        cData = (cName+1)*cKeys*sizeof(WCHAR);
        pData = (LPBYTE)LocalAlloc(LMEM_FIXED, cData);
        if(!pData)
        {
            hr = E_OUTOFMEMORY;
            _JumpError(hr, error, "LocalAlloc");
        }

        pTmp = pData;

        for(nIndex=0;nIndex<cKeys; nIndex++)
        {
            cName = cData;
            hr = RegEnumKeyEx(
                    hKey,
                    nIndex,
                    (LPWSTR)pTmp,
                    &cName,
                    0, NULL, NULL, NULL);
            _JumpIfError(hr, error, "RegEnumKeyEx");
            pTmp = pTmp+(wcslen((LPWSTR)pTmp)+1)*sizeof(WCHAR);
        }

        *(LPWSTR)pTmp= L'\0';

        hr = myRegValueToVariant(
                dwType,
                cData,
                pData,
                pVariant);
        _JumpIfError(hr, error, "myRegValueToVariant");
    }
    else
    {
        hr = RegQueryValueEx(
                hKey,
                pcwszValue,
                NULL,
                &dwType,
                NULL,
                &cData);
        _JumpIfError2(hr, error, "RegQueryValueEx", ERROR_FILE_NOT_FOUND);

        pData = (LPBYTE)LocalAlloc(LMEM_FIXED, cData);
        if(!pData)
        {
            hr = E_OUTOFMEMORY;
            _JumpError(hr, error, "LocalAlloc");
        }

        hr = RegQueryValueEx(
                hKey,
                pcwszValue,
                NULL,
                &dwType,
                pData,
                &cData);
        _JumpIfError(hr, error, "RegQueryValueEx");

        hr = myRegValueToVariant(
                dwType,
                cData,
                pData,
                pVariant);
        _JumpIfError(hr, error, "myRegValueToVariant");
    }

error:
    if(hKeyTmp)
        RegCloseKey(hKeyTmp);
    if(pData)
        LocalFree(pData);
    return myHError(hr);
}


// If variant type is VT_EMPTY, SetEntry deletes the value. Otherwise it
// set the value, see myRegValueToVariant for supported types
HRESULT CConfigStorage::SetEntry(
    LPCWSTR pcwszAuthorityName,
    LPCWSTR pcwszRelativeNodePath,
    LPCWSTR pcwszValue,
    VARIANT *pVariant)
{
    HRESULT hr = S_OK;
    HKEY hKey = NULL;
    LPBYTE pData = NULL;
    DWORD cData;
    HKEY hKeyTmp = NULL;
    DWORD dwType;

    if(EmptyString(pcwszAuthorityName))
    {
        if(!EmptyString(pcwszRelativeNodePath))
        {
            hr = E_INVALIDARG;
            _JumpError(hr, error, "CConfigStorage::GetEntry");
        }

        hr = InitRootKey();
        _JumpIfError(hr, error, "CConfigStorage::InitRootKey");

        hKey = m_hRootConfigKey;
    }
    else
    {
        hr = InitCAKey(pcwszAuthorityName);
        _JumpIfError(hr, error, "CConfigStorage::InitCAKey");

        hKey = m_hCAKey;
    }
    
    CSASSERT(hKey);

    if(!EmptyString(pcwszRelativeNodePath))
    {
        hr = RegOpenKeyEx(
               hKey,
               pcwszRelativeNodePath,
               0,
               KEY_ALL_ACCESS,
               &hKeyTmp);
        _JumpIfErrorStr(hr, error, "RegOpenKeyEx", pcwszRelativeNodePath);
        hKey = hKeyTmp;
    }

    if(VT_EMPTY == V_VT(pVariant))
    {
        // delete value
        hr = RegDeleteValue(
            hKey,
            pcwszValue);
        _JumpIfErrorStr(hr, error, "RegDeleteValue", pcwszValue);
    }
    else
    {
        // set value
        hr = myVariantToRegValue(
                pVariant,
                &dwType,
                &cData,
                &pData);
        _JumpIfError(hr, error, "myVariantToRegValue");

        hr = RegSetValueEx(
                hKey,
                pcwszValue,
                NULL,
                dwType,
                pData,
                cData);
        _JumpIfErrorStr(hr, error, "RegSetValueEx", pcwszValue);
    }

error:
    if(hKeyTmp)
        RegCloseKey(hKeyTmp);
    if(pData)
        LocalFree(pData);

    return myHError(hr);
}

HRESULT CConfigStorage::InitRootKey()
{
    HRESULT hr = S_OK;

    if(!m_hRootConfigKey)
    {
        if(m_pwszMachine)
        {
            hr = RegConnectRegistry(
                    m_pwszMachine,
                    HKEY_LOCAL_MACHINE,
                    &m_hRemoteHKLM);
            _JumpIfError(hr, error, "RegConnectRegistry");


        }

        hr = RegOpenKeyEx(
                m_hRemoteHKLM?m_hRemoteHKLM:HKEY_LOCAL_MACHINE,
                wszREGKEYCONFIGPATH,
                0,
                KEY_ALL_ACCESS,
                &m_hRootConfigKey);
        if ((HRESULT) ERROR_ACCESS_DENIED == hr)
        {
            hr = RegOpenKeyEx(
                    m_hRemoteHKLM?m_hRemoteHKLM:HKEY_LOCAL_MACHINE,
                    wszREGKEYCONFIGPATH,
                    0,
                    KEY_READ,
                    &m_hRootConfigKey);
        }
        _JumpIfErrorStr(hr, error, "RegOpenKeyEx", wszREGKEYCONFIGPATH);
    }

error:
    return hr;
}

HRESULT CConfigStorage::InitCAKey(LPCWSTR pcwszAuthority)
{
    HRESULT hr = S_OK;

    if(!m_hCAKey)
    {
        hr = InitRootKey();
        _JumpIfError(hr, error, "CConfigStorage::InitRootKey");

        hr = RegOpenKeyEx(
                m_hRootConfigKey,
                pcwszAuthority,
                0,
                KEY_ALL_ACCESS,
                &m_hCAKey);
        if ((HRESULT) ERROR_ACCESS_DENIED == hr)
        {
            hr = RegOpenKeyEx(
                    m_hRootConfigKey,
                    pcwszAuthority,
                    0,
                    KEY_READ,
                    &m_hCAKey);
        }
        _JumpIfErrorStr(hr, error, "RegOpenKeyEx", pcwszAuthority);
    }

error:
    return hr;
}