/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    Regeval.c

Abstract:

    This module contains the client side wrappers for the Win32 Registry
    enumerate value APIs.  That is:

        - RegEnumValueExA
        - RegEnumValueExW

Author:

    David J. Gilman (davegi) 18-Mar-1992

Notes:

    See the notes in server\regeval.c.

--*/

#include <rpc.h>
#include "regrpc.h"
#include "client.h"



LONG
RegEnumValueA (
    HKEY hKey,
    DWORD dwIndex,
    LPSTR lpValueName,
    LPDWORD lpcbValueName,
    LPDWORD  lpReserved,
    LPDWORD lpType,
    LPBYTE lpData,
    LPDWORD lpcbData
    )

/*++

Routine Description:

    Win32 ANSI RPC wrapper for enumerating values.

--*/

{
    UNICODE_STRING      Name;
    ANSI_STRING         AnsiString;
    NTSTATUS            Status;
    LONG                Error = ERROR_SUCCESS;
    DWORD               ValueType;
    DWORD               ValueLength;
    DWORD               InputLength;
    PWSTR               UnicodeValueBuffer;
    ULONG               UnicodeValueLength;
    PSTR                AnsiValueBuffer;
    ULONG               AnsiValueLength;
    ULONG               Index;
    BOOLEAN             Win95Server = FALSE;
    ULONG               cbAnsi = 0;
    HKEY                TempHandle = NULL;

#if DBG
    if ( BreakPointOnEntry ) {
        DbgBreakPoint();
    }
#endif


    //
    // Validate dependency between lpData and lpcbData parameters.
    //

    if( ARGUMENT_PRESENT( lpReserved ) ||
        (ARGUMENT_PRESENT( lpData ) && ( ! ARGUMENT_PRESENT( lpcbData ))) ||
        (!ARGUMENT_PRESENT(lpcbValueName)) || (!ARGUMENT_PRESENT(lpValueName)) ) {
        return ERROR_INVALID_PARAMETER;
    }

    hKey = MapPredefinedHandle( hKey, &TempHandle );
    if( hKey == NULL ) {
        Error =  ERROR_INVALID_HANDLE;
        goto ExitCleanup;
    }

    //
    // Allocate temporary buffer for the Name
    //
    Name.Length        = 0;
    Name.MaximumLength = (USHORT)((*lpcbValueName + 1) * sizeof( WCHAR ));
    Name.Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, Name.MaximumLength );
    if( Name.Buffer == NULL ) {
        Error = ERROR_NOT_ENOUGH_MEMORY;
        goto ExitCleanup;
    }

    //
    // Call the Base API passing it a pointer to the counted Unicode
    // strings for the value name. Note that zero bytes are transmitted (i.e.
    // InputLength = 0) for the data.
    //

    if (ARGUMENT_PRESENT( lpcbData )) {
        ValueLength = *lpcbData;
        }
    else {
        ValueLength = 0;
        }

    InputLength = 0;

    if( IsLocalHandle( hKey )) {

        Error = (LONG)LocalBaseRegEnumValue (
                            hKey,
                            dwIndex,
                            &Name,
                            &ValueType,
                            lpData,
                            &ValueLength,
                            &InputLength
                            );

        ASSERT( Name.Buffer );

    } else {
        DWORD dwVersion;

        //
        // Check for a downlevel Win95 server, which requires
        // us to work around their BaseRegEnumValue bugs.
        // The returned ValueLength is one WCHAR too large AND
        // they trash two bytes beyond the end of the buffer
        // for REG_SZ, REG_MULTI_SZ, and REG_EXPAND_SZ
        //
        Win95Server = IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion);

        if (Win95Server) {
            LPBYTE lpWin95Data;
            //
            // This is a Win95 server.
            // Allocate a new buffer that is two bytes larger than
            // the old one so they can trash the last two bytes.
            //
            lpWin95Data = RtlAllocateHeap(RtlProcessHeap(),
                                          0,
                                          ValueLength+sizeof(WCHAR));
            if (lpWin95Data == NULL) {
                Error = ERROR_NOT_ENOUGH_MEMORY;
            } else {
                Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ),
                                                dwIndex,
                                                &Name,
                                                &ValueType,
                                                lpWin95Data,
                                                &ValueLength,
                                                &InputLength);
                if (Error == ERROR_SUCCESS) {
                    if ((ValueType == REG_SZ) ||
                        (ValueType == REG_MULTI_SZ) ||
                        (ValueType == REG_EXPAND_SZ)) {
                        //
                        // The returned length is one WCHAR too large
                        // and the last two bytes of the buffer are trashed.
                        //
                        ValueLength -= sizeof(WCHAR);
                    }
                    CopyMemory(lpData, lpWin95Data, ValueLength);
                }
                RtlFreeHeap(RtlProcessHeap(),0,lpWin95Data);
            }

        } else {
            Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ),
                                            dwIndex,
                                            &Name,
                                            &ValueType,
                                            lpData,
                                            &ValueLength,
                                            &InputLength);
        }

    }


    //
    // If no error or callers buffer too small, and type is one of the null
    // terminated string types, then do the UNICODE to ANSI translation.
    // We handle the buffer too small case, because the callers buffer may
    // be big enough for the ANSI representation, but not the UNICODE one.
    // In this case, we need to allocate a buffer big enough, do the query
    // again and then the translation into the callers buffer.
    //

    if ((Error == ERROR_SUCCESS || Error == ERROR_MORE_DATA) &&
        ARGUMENT_PRESENT( lpcbData ) &&
        (ValueType == REG_SZ ||
         ValueType == REG_EXPAND_SZ ||
         ValueType == REG_MULTI_SZ)
       ) {

        UnicodeValueLength         = ValueLength;

        AnsiValueBuffer        = lpData;
        AnsiValueLength        = ARGUMENT_PRESENT( lpcbData )? *lpcbData : 0;


        //
        // Allocate a buffer for the UNICODE value and reissue the query.
        //
        UnicodeValueBuffer = RtlAllocateHeap( RtlProcessHeap(), 0,
                                          UnicodeValueLength
                                        );
        if (UnicodeValueBuffer == NULL) {
            Error = ERROR_NOT_ENOUGH_MEMORY;
        } else {
            InputLength = 0;

            if( IsLocalHandle( hKey )) {


                Error = (LONG)LocalBaseRegEnumValue (
                                    hKey,
                                    dwIndex,
                                    &Name,
                                    &ValueType,
                                    (LPBYTE)UnicodeValueBuffer,
                                    &ValueLength,
                                    &InputLength
                                    );
                //
                //  Make sure that the local side didn't destroy the
                //  Buffer in the Name
                //

                ASSERT(Name.Buffer);

            } else {
                if (Win95Server) {
                    LPBYTE lpWin95Data;
                    //
                    // This is a Win95 server.
                    // Allocate a new buffer that is two bytes larger than
                    // the old one so they can trash the last two bytes.
                    //
                    lpWin95Data = RtlAllocateHeap(RtlProcessHeap(),
                                                  0,
                                                  ValueLength+sizeof(WCHAR));
                    if (lpWin95Data == NULL) {
                        Error = ERROR_NOT_ENOUGH_MEMORY;
                    } else {
                        Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ),
                                                        dwIndex,
                                                        &Name,
                                                        &ValueType,
                                                        lpWin95Data,
                                                        &ValueLength,
                                                        &InputLength);
                        if (Error == ERROR_SUCCESS) {
                            if ((ValueType == REG_SZ) ||
                                (ValueType == REG_MULTI_SZ) ||
                                (ValueType == REG_EXPAND_SZ)) {
                                //
                                // The returned length is one WCHAR too large
                                // and the last two bytes of the buffer are trashed.
                                //
                                ValueLength -= sizeof(WCHAR);
                            }
                            CopyMemory(UnicodeValueBuffer, lpWin95Data, ValueLength);
                        }
                        RtlFreeHeap(RtlProcessHeap(),0,lpWin95Data);
                    }

                } else {
                    Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ),
                                                    dwIndex,
                                                    &Name,
                                                    &ValueType,
                                                    (LPBYTE)UnicodeValueBuffer,
                                                    &ValueLength,
                                                    &InputLength);
                }
            }
            // Compute needed buffer size , cbAnsi will keeps the byte
            // counts to keep MBCS string after following step.

            RtlUnicodeToMultiByteSize( &cbAnsi ,
                                       UnicodeValueBuffer ,
                                       ValueLength );

            // If we could not store all MBCS string to buffer that
            // Apps gives me. We set ERROR_MORE_DATA to Error

            if( ARGUMENT_PRESENT( lpcbData ) ) {
                if( cbAnsi > *lpcbData && lpData != NULL ) {
                    Error = ERROR_MORE_DATA;
                }
            }
        }

        if ((Error == ERROR_SUCCESS) && (AnsiValueBuffer != NULL)) {

            //
            // We have a UNICODE value, so translate it to ANSI in the callers
            // buffer.  In the case where the caller's buffer was big enough
            // for the UNICODE version, we do the conversion in place, which
            // works since the ANSI version is smaller than the UNICODE version.
            //


            Index = 0;
            Status = RtlUnicodeToMultiByteN( AnsiValueBuffer,
                                             AnsiValueLength,
                                             &Index,
                                             UnicodeValueBuffer,
                                             UnicodeValueLength
                                           );

            if (!NT_SUCCESS( Status )) {
                Error = RtlNtStatusToDosError( Status );
            }
            cbAnsi = Index;
        }

        //
        // Free the unicode buffer if it was successfully allocated
        //
        if (UnicodeValueBuffer != NULL) {
            RtlFreeHeap( RtlProcessHeap(), 0, UnicodeValueBuffer );
        }

        //
        // Return the length of the ANSI version to the caller.
        //

        ValueLength = cbAnsi;

        //
        // Special hack to help out all the peopl who
        // believe the length of a NULL terminated string is
        // strlen(foo) instead of strlen(foo) + 1.
        // If the last character of the buffer is not a NULL
        // and there is enough space left in the caller's buffer,
        // slap a NULL in there to prevent him from going nuts
        // trying to do a strlen().
        //
        if (ARGUMENT_PRESENT( lpData ) &&
            (*lpcbData > ValueLength)  &&
            (ValueLength > 0) &&
            (lpData[ValueLength-1] != '\0')) {

            lpData[ValueLength] = '\0';
        }
    }

    //
    // Return the value type and data length if requested and we have it.
    //

    if (Error == ERROR_SUCCESS || Error == ERROR_MORE_DATA) {

        if (lpcbData != NULL) {
            *lpcbData = ValueLength;
        }

        if (lpType != NULL) {
            *lpType = ValueType;
        }
    }

    //
    // If the information was not succesfully queried return the error.
    //

    if( Error != ERROR_SUCCESS ) {
        // free allocated buffer
        RtlFreeHeap( RtlProcessHeap(), 0, Name.Buffer );
        goto ExitCleanup;
    }


    //
    //  Subtract the NULL from the Length. This was added by the server
    //  so that RPC would transmit it.
    //

    if ( Name.Length > 0 ) {
        Name.Length -= sizeof( UNICODE_NULL );
    }

    //
    // Convert the name to ANSI.
    //

    AnsiString.MaximumLength    = ( USHORT ) *lpcbValueName;
    AnsiString.Buffer           = lpValueName;

    Status = RtlUnicodeStringToAnsiString(
                &AnsiString,
                &Name,
                FALSE
                );


    // free allocated buffer
    RtlFreeHeap( RtlProcessHeap(), 0, Name.Buffer );
    //
    // If the name conversion failed, map and return the error.
    //

    if( ! NT_SUCCESS( Status )) {


        Error = RtlNtStatusToDosError( Status );
        goto ExitCleanup;
    }

    //
    // Update the name length return parameter.
    //

    *lpcbValueName = AnsiString.Length;


ExitCleanup:
    
    CLOSE_LOCAL_HANDLE(TempHandle);
    return Error;
}



LONG
RegEnumValueW (
    HKEY hKey,
    DWORD dwIndex,
    LPWSTR lpValueName,
    LPDWORD lpcbValueName,
    LPDWORD lpReserved,
    LPDWORD lpType,
    LPBYTE lpData,
    LPDWORD lpcbData
    )

/*++

Routine Description:

    Win32 Unicode RPC wrapper for enumerating values.

--*/

{
    UNICODE_STRING      Name;
    LONG                Error;
    DWORD               InputLength;
    DWORD               ValueLength;
    DWORD               ValueType;
    HKEY                TempHandle = NULL;


#if DBG
    if ( BreakPointOnEntry ) {
        DbgBreakPoint();
    }
#endif

    //
    // Validate dependency between lpData and lpcbData parameters.
    //

    if( ARGUMENT_PRESENT( lpReserved ) ||
        (ARGUMENT_PRESENT( lpData ) && ( ! ARGUMENT_PRESENT( lpcbData ))) ||
        (!ARGUMENT_PRESENT(lpcbValueName)) || (!ARGUMENT_PRESENT(lpValueName)) ) {
        return ERROR_INVALID_PARAMETER;
    }

    hKey = MapPredefinedHandle( hKey, &TempHandle );
    if( hKey == NULL ) {
        Error = ERROR_INVALID_HANDLE;
        goto ExitCleanup;
    }

    Name.Length           = 0;
    Name.MaximumLength    = ( USHORT )( *lpcbValueName << 1 );
    Name.Buffer           = lpValueName;

    //
    // Call the Base API passing it a pointer to the counted Unicode
    // string for the name and return the results. Note that zero bytes
    // are transmitted (i.e.InputLength = 0) for the data.
    //

    InputLength = 0;
    ValueLength = ( ARGUMENT_PRESENT( lpcbData ) )? *lpcbData : 0;

    if( IsLocalHandle( hKey )) {

        Error = (LONG)LocalBaseRegEnumValue (
                            hKey,
                            dwIndex,
                            &Name,
                            &ValueType,
                            lpData,
                            &ValueLength,
                            &InputLength
                            );
    } else {
        DWORD dwVersion;

        if (IsWin95Server(DereferenceRemoteHandle(hKey),dwVersion)) {
            LPBYTE lpWin95Data;
            //
            // This is a Win95 server.
            // Allocate a new buffer that is two bytes larger than
            // the old one so they can trash the last two bytes.
            //
            lpWin95Data = RtlAllocateHeap(RtlProcessHeap(),
                                          0,
                                          ValueLength+sizeof(WCHAR));
            if (lpWin95Data == NULL) {
                Error = ERROR_NOT_ENOUGH_MEMORY;
                goto ExitCleanup;
            } else {
                Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ),
                                                dwIndex,
                                                &Name,
                                                &ValueType,
                                                lpWin95Data,
                                                &ValueLength,
                                                &InputLength);
                if (Error == ERROR_SUCCESS) {
                    if ((ValueType == REG_SZ) ||
                        (ValueType == REG_MULTI_SZ) ||
                        (ValueType == REG_EXPAND_SZ)) {
                        //
                        // The returned length is one WCHAR too large
                        // and the last two bytes of the buffer are trashed.
                        //
                        ValueLength -= sizeof(WCHAR);
                    }
                    CopyMemory(lpData, lpWin95Data, ValueLength);
                }
                RtlFreeHeap(RtlProcessHeap(),0,lpWin95Data);
            }

        } else {
            Error = (LONG)BaseRegEnumValue (DereferenceRemoteHandle( hKey ),
                                            dwIndex,
                                            &Name,
                                            &ValueType,
                                            lpData,
                                            &ValueLength,
                                            &InputLength);
        }
    }
    //
    // Special hack to help out all the people who
    // believe the length of a NULL terminated string is
    // strlen(foo) instead of strlen(foo) + 1.
    // If the last character of the buffer is not a NULL
    // and there is enough space left in the caller's buffer,
    // slap a NULL in there to prevent him from going nuts
    // trying to do a strlen().
    //
    if ( (Error == ERROR_SUCCESS) &&
         ARGUMENT_PRESENT( lpData ) &&
         ( (ValueType == REG_SZ) ||
           (ValueType == REG_EXPAND_SZ) ||
           (ValueType == REG_MULTI_SZ)) &&
         ( ValueLength > sizeof(WCHAR))) {

        UNALIGNED WCHAR *String = (UNALIGNED WCHAR *)lpData;
        DWORD Length = ValueLength/sizeof(WCHAR);

        if ((String[Length-1] != UNICODE_NULL) &&
            (ValueLength+sizeof(WCHAR) <= *lpcbData)) {
            String[Length] = UNICODE_NULL;
        }
    }

    //
    // Don't count the NUL.
    //
    if( Name.Length != 0 ) {
        *lpcbValueName = ( Name.Length >> 1 ) - 1;
    }

    if( ARGUMENT_PRESENT( lpcbData ) ) {
        *lpcbData = ValueLength;
    }
    if ( ARGUMENT_PRESENT( lpType )) {
        *lpType = ValueType;
    }

ExitCleanup:
    
    CLOSE_LOCAL_HANDLE(TempHandle);
    return Error;
}
