/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    reg.cxx

Abstract:

    Contains code that implements REGISTRY_OBJ class defined in reg.hxx.

Author:

    Madan Appiah (madana)  19-Dec-1994

Environment:

    User Mode - Win32

Revision History:
    Sean Woodward (t-seanwo) 26-October-1997    ADSI Update

--*/

#include <svcloc.hxx>

MEMORY *CacheHeap = NULL;

INLINE
DWORD
REGISTRY_OBJ::GetValueSizeAndType(
    LPWSTR ValueName,
    LPDWORD ValueSize,
    LPDWORD ValueType
    )
/*++

Routine Description:

    This function returns the size and type of a value of this key.

Arguments:

    ValueName : name of the value whose size and type returned.

    ValueSize : pointer to a location where the value size is returned.

    ValueType : pointer to a location where the value type is returned.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;

    Error = RegQueryValueExW(
                _RegHandle,
                ValueName,
                0,
                ValueType,
                NULL,
                ValueSize );

    return( Error );
}


REGISTRY_OBJ::REGISTRY_OBJ(
    HKEY Handle,
    DWORD Error
    )
/*++

Routine Description:

    This function is a inline function that initialize the registry
    object with given handle and status.

Arguments:

    Handle : registry object handle value.

    Error : registry object status value.

Return Value:

    None.

--*/
{
    _RegHandle = Handle;
    _Status = Error;
    _Index = 0;
    _ValIndex = 0;
    return;
};


REGISTRY_OBJ::REGISTRY_OBJ(
    HKEY ParentHandle,
    LPWSTR KeyName
    )
/*++

Routine Description:

    Initializes the registry object from its parent's registry key
    handle and this object's keyname.

Arguments:

    ParentHandle : registry handle of the parent key.

    Keyname : key name of the new registry object being created.

Return Value:

    None.

--*/
{
    _Index = 0;
    _ValIndex = 0;
    _Status = RegOpenKeyExW(
                ParentHandle,
                KeyName,
                0,
                DEFAULT_KEY_ACCESS,
                &_RegHandle );

    if( _Status != ERROR_SUCCESS ) {
        _RegHandle = NULL;
    }

    return;
}

REGISTRY_OBJ::REGISTRY_OBJ(
    REGISTRY_OBJ *ParentObj,
    LPWSTR KeyName
    )
/*++

Routine Description:

    Initializes the registry object from its parent's registry object
    and this object's keyname.

Arguments:

    ParentObj : registry object of the parent.

    Keyname : key name of the new registry object being created.

Return Value:

    None.

--*/
{
    _Index = 0;
    _ValIndex = 0;
    _Status = RegOpenKeyExW(
                ParentObj->_RegHandle,
                KeyName,
                0,
                DEFAULT_KEY_ACCESS,
                &_RegHandle );

    if( _Status != ERROR_SUCCESS ) {
        _RegHandle = NULL;
    }

    return;
}

DWORD
REGISTRY_OBJ::Create(
    LPWSTR ChildName
    )
/*++

Routine Description:

    Creates a new subkey under this key.

Arguments:

    ChildName : name of the subkey being created.

Return Value:

    Windows Error Code.

--*/
{
    HKEY ChildHandle;
    DWORD KeyDisposition;

    _Status = RegCreateKeyExW(
               _RegHandle,
               ChildName,
               0,
               DEFAULT_CLASS,
               REG_OPTION_NON_VOLATILE,
               DEFAULT_KEY_ACCESS,
               NULL,
               &ChildHandle,
               &KeyDisposition );

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

    if( KeyDisposition == REG_CREATED_NEW_KEY ) {
        TcpsvcsDbgPrint(( DEBUG_REGISTRY,
           "Registry key (%ws) is created.\n", ChildName ));
    }

    //
    // close the child handle before return.
    //

    RegCloseKey( ChildHandle );
    return( ERROR_SUCCESS );
}


DWORD
REGISTRY_OBJ::Create(
    LPWSTR ChildName,
    REGISTRY_OBJ **ChildObj
    )
/*++

Routine Description:

    Creates a new subney and a new subney registry object.

Arguments:

    ChildName : name of the subkey being created.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    HKEY ChildHandle;
    DWORD KeyDisposition;

    Error = RegCreateKeyExW(
                _RegHandle,
                ChildName,
                0,
                DEFAULT_CLASS,
                REG_OPTION_NON_VOLATILE,
                DEFAULT_KEY_ACCESS,
                NULL,
                &ChildHandle,
                &KeyDisposition );


    if( Error != ERROR_SUCCESS ) {
        *ChildObj = new REGISTRY_OBJ( NULL, Error );
    }
    else {

        if( KeyDisposition == REG_CREATED_NEW_KEY ) {
            TcpsvcsDbgPrint(( DEBUG_REGISTRY,
               "Registry key (%ws) is created.\n", ChildName ));
        }

        *ChildObj = new REGISTRY_OBJ( ChildHandle, (DWORD)ERROR_SUCCESS );
    }

    return( Error );
}

DWORD
REGISTRY_OBJ::Create(
    LPWSTR ChildName,
    REGISTRY_OBJ **ChildObj,
    DWORD *KeyDisposition
    )
/*++

Routine Description:

    Creates a new subney and a new subney registry object.

Arguments:

    ChildName : name of the subkey being created.

    ChildObj : pointer to a location where the child registry object
        pointer is returned.

    KeyDisposition : pointer to a location where the child KeyDisposition
        value is returned.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    HKEY ChildHandle;

    Error = RegCreateKeyExW(
                _RegHandle,
                ChildName,
                0,
                DEFAULT_CLASS,
                REG_OPTION_NON_VOLATILE,
                DEFAULT_KEY_ACCESS,
                NULL,
                &ChildHandle,
                KeyDisposition );


    if( Error != ERROR_SUCCESS ) {
        *ChildObj = new REGISTRY_OBJ( NULL, Error );
    }
    else {

        if( *KeyDisposition == REG_CREATED_NEW_KEY ) {
            TcpsvcsDbgPrint(( DEBUG_REGISTRY,
               "Registry key (%ws) is created.\n", ChildName ));
        }

        *ChildObj = new REGISTRY_OBJ( ChildHandle, (DWORD)ERROR_SUCCESS );
    }

    return( Error );
}

DWORD
REGISTRY_OBJ::GetValue(
    LPWSTR ValueName,
    DWORD *Data
    )
/*++

Routine Description:

    Gets a REG_DWORD value.

Arguments:

    ValueName : name of the value being retrived.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    DWORD ValueType;
    DWORD ValueSize = sizeof(DWORD);

    Error = RegQueryValueExW(
                _RegHandle,
                ValueName,
                0,
                &ValueType,
                (LPBYTE)Data,
                &ValueSize );

    TcpsvcsDbgAssert( ValueSize == sizeof( DWORD ) );
    TcpsvcsDbgAssert( ValueType == REG_DWORD );

    return( Error );
}

DWORD
REGISTRY_OBJ::GetValue(
    LPWSTR ValueName,
    LPWSTR *Data,
    DWORD *NumStrings
    )
/*++

Routine Description:

    Gets a REG_SZ or REG_EXPAND_SZ or REG_MULTI_SZ value.

Arguments:

    ValueName : name of the value being retrived.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    DWORD ValueType;
    DWORD ValueSize;
    LPBYTE StringData = NULL;

    Error = GetValueSizeAndType( ValueName, &ValueSize, &ValueType );

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

    TcpsvcsDbgAssert(
        (ValueType == REG_SZ) ||
        (ValueType == REG_EXPAND_SZ) ||
        (ValueType == REG_MULTI_SZ) );

    StringData = (LPBYTE)CacheHeap->Alloc( ValueSize );

    if( StringData == NULL ) {
        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    Error = RegQueryValueExW(
                _RegHandle,
                ValueName,
                0,
                &ValueType,
                StringData,
                &ValueSize );

    if( Error != ERROR_SUCCESS ) {
        CacheHeap->Free( StringData );
        return( Error );
    }

    *Data = (LPWSTR)StringData;

    if( (ValueType == REG_SZ) || (ValueType == REG_EXPAND_SZ) ) {
        *NumStrings = 1;
    }
    else {

        DWORD Strings = 0;
        LPWSTR StrPtr = (LPWSTR)StringData;
        DWORD Len;

        while( (Len = wcslen(StrPtr)) != 0 ) {
            Strings++;
            StrPtr = StrPtr + Len + 1;
        }

        *NumStrings = Strings;
    }

    return( ERROR_SUCCESS );
}

DWORD
REGISTRY_OBJ::GetValue(
    LPWSTR ValueName,
    LPBYTE *Data,
    DWORD *DataLen
    )
/*++

Routine Description:

    Gets a REG_BINARY value.

Arguments:

    ValueName : name of the value being retrived.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    DWORD ValueType;
    DWORD ValueSize;
    LPBYTE BinaryData = NULL;

    Error = GetValueSizeAndType( ValueName, &ValueSize, &ValueType );

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

    TcpsvcsDbgAssert( ValueType == REG_BINARY );

    BinaryData = (LPBYTE)CacheHeap->Alloc( ValueSize );

    if( BinaryData == NULL ) {
        return( ERROR_NOT_ENOUGH_MEMORY );
    }

    Error = RegQueryValueExW(
                _RegHandle,
                ValueName,
                0,
                &ValueType,
                BinaryData,
                &ValueSize );

    if( Error != ERROR_SUCCESS ) {
        CacheHeap->Free( BinaryData );
        return( Error );
    }

    *Data = BinaryData;
    *DataLen = ValueSize;
    return( ERROR_SUCCESS );
}

DWORD
REGISTRY_OBJ::GetValue(
    LPWSTR ValueName,
    LPBYTE Data,
    DWORD *DataLen
    )
/*++

Routine Description:

    Gets a REG_BINARY value.

Arguments:

    ValueName : name of the value being retrived.

    Data : pointer to a buffer where the data will be read.

    Datalen : pointer to location where length of the above buffer is
        passed. On return this location will have the length of the
        data read.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    DWORD ValueType;

    Error = RegQueryValueExW(
                _RegHandle,
                ValueName,
                0,
                &ValueType,
                Data,
                DataLen );

    return( Error );
}

DWORD
REGISTRY_OBJ::SetValue(
    LPWSTR ValueName,
    LPDWORD Data
    )
/*++

Routine Description:

    Sets a REG_DWORD value.

Arguments:

    ValueName : name of the value being set.

    Date : pointer to a DWORD data.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;

    Error = RegSetValueExW(
                _RegHandle,
                ValueName,
                0,
                REG_DWORD,
                (LPBYTE)Data,
                sizeof(DWORD) );

    return( Error );
}

DWORD
REGISTRY_OBJ::SetValue(
    LPWSTR ValueName,
    LPWSTR Data,
    DWORD StringType
    )
/*++

Routine Description:

    Sets a REG_SZ or REG_EXPAND_SZ or REG_MULTI_SZ value.

    Data : pointer to STRING(s) data.

    StringType : type of string data in the above buffer, it should be
        either of the following types :
            REG_SZ or REG_EXPAND_SZ or REG_MULTI_SZ

Arguments:

    ValueName : name of the value being set.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;

    Error = RegSetValueExW(
                _RegHandle,
                ValueName,
                0,
                StringType,
                (LPBYTE)Data,
                sizeof(WCHAR) * (wcslen(Data) + 1) );

    return( Error );
}

DWORD
REGISTRY_OBJ::SetValue(
    LPSTR ValueName,
    LPSTR Data,
    DWORD DataLen,
    DWORD StringType
    )
/*++

Routine Description:

    Sets a REG_SZ or REG_EXPAND_SZ or REG_MULTI_SZ value.

    Data : pointer to STRING(s) data.

    DataLen : data length

    StringType : type of string data in the above buffer, it should be
        either of the following types :
            REG_SZ or REG_EXPAND_SZ or REG_MULTI_SZ

Arguments:

    ValueName : name of the value being set.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;

    Error = RegSetValueEx(
                _RegHandle,
                ValueName,
                0,
                StringType,
                (LPBYTE)Data,
                DataLen );

    return( Error );
}

DWORD
REGISTRY_OBJ::SetValue(
    LPWSTR ValueName,
    LPBYTE Data,
    DWORD DataLen
    )
/*++

Routine Description:

    Sets a REG_BINARY value.

Arguments:

    ValueName : name of the value being set.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;

    Error = RegSetValueExW(
                _RegHandle,
                ValueName,
                0,
                REG_BINARY,
                Data,
                DataLen );

    return( Error );
}

DWORD
REGISTRY_OBJ::FindNextKey(
    LPWSTR Key,
    DWORD KeySize
    )
/*++

Routine Description:

    Retrieves the Next subkey name of this key.

Arguments:

    Key - pointer to a buffer that receives the subkey name.

    KeySize - size of the above buffer in CHARS.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    DWORD KeyLength;
    FILETIME KeyLastWrite;

    KeyLength = KeySize * sizeof(WCHAR);
    Error = RegEnumKeyExW(
                _RegHandle,
                _Index,
                Key,
                &KeyLength,
                0,                  // reserved.
                NULL,               // class string not required.
                0,                  // class string buffer size.
                &KeyLastWrite );

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

    TcpsvcsDbgAssert( KeyLength <= KeySize );

    //
    // increament the index to point to the next key.
    //

    _Index++;
    return( ERROR_SUCCESS );
}

DWORD
REGISTRY_OBJ::DeleteKey(
    LPWSTR ChildKeyName
    )
/*++

Routine Description:

    Deletes a subkey node.

Arguments:

    ChildKeyName : name of the subkey to be deleted.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    LPWSTR GChildKeyName[MAX_KEY_SIZE];
    REGISTRY_OBJ ChildObj( _RegHandle, ChildKeyName );

    Error = ChildObj.GetStatus();

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

    //
    // delete all its subkeys.
    //

    Error = ChildObj.FindFirstKey(
                (LPWSTR)GChildKeyName,
                MAX_KEY_SIZE );

    while( Error == ERROR_SUCCESS ) {

        Error = ChildObj.DeleteKey( (LPWSTR)GChildKeyName );

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

        Error = ChildObj.FindFirstKey(
                    (LPWSTR)GChildKeyName,
                    MAX_KEY_SIZE );
    }

    if( Error != ERROR_NO_MORE_ITEMS ) {
        return( Error );
    }

    //
    // delete this key.
    //

    Error = RegDeleteKeyW( _RegHandle, (LPWSTR)ChildKeyName );
    return( Error );
}

DWORD
REGISTRY_OBJ::FindNextValue(
    LPSTR ValueName,
    DWORD ValueSize,
    LPBYTE Data,
    DWORD *DataLen
    )
/*++

Routine Description:

    Retrieves the Next value name of this key.

Arguments:

    ValueName - pointer to a buffer that receives the Value name.

    ValueSize - size of the above buffer in CHARS.
    Data - pointer to a buffer that receives the Value data.
    DataLen - pointer to a buffer that receives data size.

Return Value:

    Windows Error Code.

--*/
{
    DWORD Error;
    DWORD ValueLength;
    DWORD ValueType;

    ValueLength = ValueSize * sizeof(CHAR);

    Error = RegEnumValue(
                _RegHandle,
                _ValIndex,
                ValueName,
                &ValueLength,
                NULL,                  // reserved.
                &ValueType,
                Data,
                DataLen );

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

    TcpsvcsDbgAssert( ValueLength <= ValueSize );

    //
    // increment the value index to point to the next value.
    //

    _ValIndex++;
    return( ERROR_SUCCESS );
}