/*++




Copyright (c) 1992  Microsoft Corporation

Module Name:

    Regclass.c

Abstract:

    This module contains the client side wrappers for the Win32 Registry
    APIs to open the classes root key for a specified user.

        - RegOpenUserClassesRoot

Author:

    Adam Edwards (adamed) 15-Apr-1998

Notes:

    This API is local only.
    See the notes in server\regkey.c.

--*/

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

#define REG_USER_CLASSES_PREFIX L"\\Registry\\User\\"
#define REG_USER_CLASSES_SUFFIX L"_Classes"

BOOL InitializeClassesEnumTable();
BOOL InitializeClassesNameSpace();

BOOL CleanupClassesEnumTable(DWORD dwCriteria);
BOOL CleanupClassesNameSpace();

#if defined(LEAK_TRACK)
NTSTATUS TrackObject(HKEY hKey);
#endif // defined(LEAK_TRACK)

extern BOOL gbCombinedClasses;


LONG
APIENTRY
RegOpenUserClassesRoot(
    HANDLE hToken,
    DWORD  dwOptions,
    REGSAM samDesired,
    PHKEY  phkResult
    )

/*++

Routine Description:

    Win32 Unicode RPC wrapper for opening the classes root key
    for the use specified by the hToken parameter.

Arguments:

    hToken - token for user whose classes root is to be opened. If 
        this parameter is NULL, we return ERROR_INVALID_PARAMETER

    phkResult - Returns an open handle to the newly opened key.

Return Value:

    Returns ERROR_SUCCESS (0) for success; error-code for failure.

Notes:


--*/

{
    NTSTATUS            Status;
    UNICODE_STRING      UsersHive;
    BYTE achBuffer[100];
    PTOKEN_USER pTokenInfo = (PTOKEN_USER) &achBuffer[0];
    DWORD dwBytesRequired;
    LONG Error;

    //
    //  Caller must pass pointer to the variable where the opened handle
    //  will be returned
    //

    if( phkResult == NULL ) {
        return ERROR_INVALID_PARAMETER;
    }

    if (NULL == hToken) {
        return ERROR_INVALID_PARAMETER;
    }

    if (dwOptions != REG_OPTION_RESERVED) {
        return ERROR_INVALID_PARAMETER;
    }

    if (!gbCombinedClasses) {
        return ERROR_FILE_NOT_FOUND;
    }
    
    //
    // open up the token to get the sid
    //

    if (!GetTokenInformation(
        hToken,                    // Handle
        TokenUser,                 // TokenInformationClass
        pTokenInfo,                // TokenInformation
        sizeof(achBuffer),         // TokenInformationLength
        &dwBytesRequired           // ReturnLength
        )) {

        Error = GetLastError();

        //
        // Try again if the buffer was too small
        //

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

        //
        // Allocate space for the user info
        //
        pTokenInfo = (PTOKEN_USER) RtlAllocateHeap( RtlProcessHeap(), 0, dwBytesRequired);

        if (!pTokenInfo) {
            return ERROR_NOT_ENOUGH_MEMORY;
        }


        //
        // Read in the UserInfo
        //

        if (!GetTokenInformation(
            hToken,                // Handle
            TokenUser,                 // TokenInformationClass
            pTokenInfo,                // TokenInformation
            dwBytesRequired,           // TokenInformationLength
            &dwBytesRequired           // ReturnLength
            )) {

            RtlFreeHeap( RtlProcessHeap(), 0, pTokenInfo );
            return GetLastError();
        }
    }

    //
    //  Change sid to a string
    //

    Status = RtlConvertSidToUnicodeString(
        &UsersHive,
        pTokenInfo->User.Sid,
        TRUE); // allocate the string

    if (NT_SUCCESS(Status)) {
        
        UNICODE_STRING UserClassesString;

        UserClassesString.MaximumLength = UsersHive.Length + 
            sizeof(REG_USER_CLASSES_PREFIX) + 
            sizeof(REG_USER_CLASSES_SUFFIX);

        UserClassesString.Buffer = (WCHAR*)RtlAllocateHeap( RtlProcessHeap(), 0, UserClassesString.MaximumLength);

        if (UserClassesString.Buffer) {

            UNICODE_STRING UserPrefix;

            //
            // construct the name
            //

            RtlInitUnicodeString(&UserPrefix, REG_USER_CLASSES_PREFIX);

            RtlCopyUnicodeString(&UserClassesString, &UserPrefix);

            Status = RtlAppendUnicodeStringToString(&UserClassesString, &UsersHive);

            if (NT_SUCCESS(Status)) {
                Status = RtlAppendUnicodeToString(&UserClassesString, 
                                                  REG_USER_CLASSES_SUFFIX);
            }

            if (NT_SUCCESS(Status)) {

                OBJECT_ATTRIBUTES Obja;

                // open this key
                InitializeObjectAttributes(
                    &Obja,
                    &UserClassesString,
                    OBJ_CASE_INSENSITIVE,
                    NULL, // using absolute path, no hkey
                    NULL);

                Status = NtOpenKey(
                    phkResult,
                    samDesired,
                    &Obja);
            }

            RtlFreeHeap( RtlProcessHeap(), 0, UserClassesString.Buffer );
        } else {
            Status = STATUS_NO_MEMORY;
        }

        RtlFreeUnicodeString(&UsersHive);

    }

    if( pTokenInfo != (PTOKEN_USER)&achBuffer[0] ) {
        RtlFreeHeap( RtlProcessHeap(), 0, pTokenInfo );
    }

    if (NT_SUCCESS(Status)) {
#if defined(LEAK_TRACK)

        if (g_RegLeakTraceInfo.bEnableLeakTrack) {
            (void) TrackObject(*phkResult);
        }
        
#endif defined(LEAK_TRACK)

        // mark this key as a class key
        TagSpecialClassesHandle(phkResult);
    }

    return RtlNtStatusToDosError(Status);
}

BOOL InitializeClassesRoot() 
{
    if (!InitializeClassesEnumTable()) {
        return FALSE;
    }

    return TRUE;
}

BOOL CleanupClassesRoot(BOOL fOnlyThisThread) 
{
    //
    // Always remove enumeration states for this thread
    //
    return CleanupClassesEnumTable( fOnlyThisThread );
}