/*++
*******************************************************************************
* Copyright (c) 1990-97 Digi International Inc., All Rights Reserved
*
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
*
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*******************************************************************************

Module Name:

   sohutil.c

Abstract:

   This module contains utilities used by DigiSoh

Author:

   Jeff Romatoski


--*/


////////////////////////////////////////////////////////////////////////////////

#include "digisoh.h"
#include "sohutil.h"

NTSTATUS
DigiSohUtilEnumSubkeys(
    IN   PUNICODE_STRING					RegistryPathName,
    IN   PWSTR								Subkey,
    IN   PUTIL_REGISTRY_CALLBACK_ROUTINE	Callback,
    IN   PVOID								Context1,
    IN   PVOID								Context2,
    IN   PVOID								Context3
)
/*++

Routine Description :

    Enumerate the subkeys in the registry entry, calling our callback routine for
    each subkey found.

Arguments :

    RegistryPathName	- The Registry Path.
	SubKey				- The subkey to enumerate.
	CallBack			- The CallBack function for each enumerated key.
	Context1			- Value to be passed back on the CallBack.
	Context2			- Value to be passed back on the CallBack.
	Context3			- Value to be passed back on the CallBack.

Return Value :

    NT status code - STATUS_SUCCESS if no problems

--*/
{
    HANDLE				hKey;
    HANDLE				hSubkey;
    OBJECT_ATTRIBUTES	oa;
    NTSTATUS			Status;
    ULONG				Index;
    UNICODE_STRING		SubkeyName;

    //  First try opening the Registry Path 
    InitializeObjectAttributes(&oa,
                               RegistryPathName,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               (PSECURITY_DESCRIPTOR)NULL);

    Status = ZwOpenKey(&hKey,
					   KEY_READ,
                       &oa);

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

	//  Open the SubKey
    RtlInitUnicodeString(&SubkeyName, Subkey);

    InitializeObjectAttributes(&oa,
                               &SubkeyName,
                               OBJ_CASE_INSENSITIVE,
                               hKey,
                               (PSECURITY_DESCRIPTOR)NULL);

    Status = ZwOpenKey(&hSubkey,
                       KEY_ENUMERATE_SUB_KEYS,
                       &oa);

    ZwClose(hKey);

    if (!NT_SUCCESS(Status)) {
        return Status;
    }

    //  Now enumerate the subkeys - calling the callback function for
    //  each one subkey found
    for (Index = 0; ; Index++) {

        KEY_BASIC_INFORMATION	Info;
        PKEY_BASIC_INFORMATION	pInfo;
        ULONG					ResultLength;
        ULONG					Size;
        PWSTR					Position;
        PWSTR					FullName;
        PWSTR					KeyName;

        //  First find the length of the subkey data
        Status = ZwEnumerateKey(hSubkey,
                                Index,
                                KeyBasicInformation,
                                &Info,
                                sizeof(Info),
                                &ResultLength);

        if (Status == STATUS_NO_MORE_ENTRIES || NT_ERROR(Status)) {
            break;
        }

        Size = Info.NameLength + FIELD_OFFSET(KEY_BASIC_INFORMATION, Name[0]);

        pInfo = (PKEY_BASIC_INFORMATION)
                ExAllocatePool(PagedPool, Size);

        if (pInfo == NULL) {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            break;
        }

        Status = ZwEnumerateKey(hSubkey,
                                Index,
                                KeyBasicInformation,
                                pInfo,
                                Size,
                                &ResultLength);

        if (!NT_SUCCESS(Status)) {
            ExFreePool((PVOID)pInfo);
            break;
        }

        if (Size != ResultLength) {

            ExFreePool((PVOID)pInfo);
            Status = STATUS_INTERNAL_ERROR;
            break;
        }

        //  Generate the fully expanded registry name for call the callback.
        FullName = ExAllocatePool(PagedPool,
                                  RegistryPathName->Length +
                                  sizeof(WCHAR) +       // '\'
                                  SubkeyName.Length +
                                  sizeof(WCHAR) +       // '\'
                                  pInfo->NameLength + sizeof(UNICODE_NULL));

        if (FullName == NULL) {
            ExFreePool((PVOID)pInfo);
            return STATUS_INSUFFICIENT_RESOURCES;
        }

		// Add the input registry path to the callback buffer
        RtlCopyMemory((PVOID)FullName,
                      (PVOID)RegistryPathName->Buffer,
                      RegistryPathName->Length);
        Position = FullName + RegistryPathName->Length / sizeof(WCHAR);

 		// Add a backslash
        Position[0] = '\\';
        Position++;

		// Add the input Subkey
        RtlCopyMemory((PVOID)Position,
                      (PVOID)Subkey,
                      SubkeyName.Length);
        Position += SubkeyName.Length / sizeof(WCHAR);

		// Add a backslash
        Position[0] = '\\';
        Position++;

		// Add the Key Name
        RtlCopyMemory((PVOID)Position,
                      (PVOID)pInfo->Name,
                      pInfo->NameLength);
        Position += pInfo->NameLength / sizeof(WCHAR);

        //  Null terminate
        Position[0] = UNICODE_NULL;

        //  Generate the enumerated key name for call the callback.
        KeyName = ExAllocatePool(PagedPool,
								 pInfo->NameLength + sizeof(UNICODE_NULL));
		if (KeyName == NULL) {
            ExFreePool((PVOID)FullName);
            ExFreePool((PVOID)pInfo);
            return STATUS_INSUFFICIENT_RESOURCES;
        }

		// Add the Key Name
        RtlCopyMemory((PVOID)KeyName,
                      (PVOID)pInfo->Name,
                      pInfo->NameLength);
        Position = KeyName + (pInfo->NameLength / sizeof(WCHAR));

        //  Null terminate
        Position[0] = UNICODE_NULL;

		// Done with the Info
        ExFreePool((PVOID)pInfo);

        //  Call back with key information
        Status = (*Callback)(FullName, KeyName, Context1, Context2, Context3);

		// Were Done with this memory
		ExFreePool(FullName);
		ExFreePool(KeyName);

        if (!NT_SUCCESS(Status)) {
            break;
        }

    }

    ZwClose(hSubkey);

    //  No SubKeys Found
    if (Index == 0 && Status == STATUS_NO_MORE_ENTRIES) {
        return STATUS_DEVICE_CONFIGURATION_ERROR;
    }

    return Status == STATUS_NO_MORE_ENTRIES ? STATUS_SUCCESS : Status;
}




