/*++

Copyright (c) 1997-2000 Microsoft Corporation

Module Name:

    wmiguidapi.c

Abstract:

   Data structures and functions that generate GUID. 


--*/



#include <ntos.h>

#define MAX_CACHED_UUID_TIME 10000  // 10 seconds
#define WMI_UUID_TIME_HIGH_MASK    0x0FFF
#define WMI_UUID_VERSION           0x1000
typedef long WMI_STATUS;
#define WMI_ENTRY __stdcall
#define WMI_S_OUT_OF_MEMORY               14
#define WMI_S_OK                          0
#define WMI_S_UUID_LOCAL_ONLY            1824L
//#define RPC_RAND_UUID_VERSION      0x4000
#define WMI_UUID_RESERVED          0x80
#define WMI_UUID_CLOCK_SEQ_HI_MASK 0x3F

extern WmipSleep(unsigned long dwMilliseconds);

typedef struct _WMI_UUID_GENERATE
{
    unsigned long  TimeLow;
    unsigned short TimeMid;
    unsigned short TimeHiAndVersion;
    unsigned char  ClockSeqHiAndReserved;
    unsigned char  ClockSeqLow;
    unsigned char  NodeId[6];
} WMI_UUID_GENERATE;

typedef struct _UUID_CACHED_VALUES_STRUCT
{

    ULARGE_INTEGER      Time;  // Time of last uuid allocation
    long                AllocatedCount; // Number of UUIDs allocated
    unsigned char       ClockSeqHiAndReserved;
    unsigned char       ClockSeqLow;

    unsigned char       NodeId[6];
} UUID_CACHED_VALUES_STRUCT;


UUID_CACHED_VALUES_STRUCT  UuidCachedValues;

WMI_STATUS 
WmipUuidGetValues(
    OUT UUID_CACHED_VALUES_STRUCT *Values
    )
/*++

Routine Description:

    This routine allocates a block of uuids for UuidCreate to handout.

Arguments:

    Values - Set to contain everything needed to allocate a block of uuids.
             The following fields will be updated here:

    NextTimeLow -   Together with LastTimeLow, this denotes the boundaries
                    of a block of Uuids. The values between NextTimeLow
                    and LastTimeLow are used in a sequence of Uuids returned
                    by UuidCreate().

    LastTimeLow -   See NextTimeLow.

    ClockSequence - Clock sequence field in the uuid.  This is changed
                    when the clock is set backward.

Return Value:

    WMI_S_OK - We successfully allocated a block of uuids.

    WMI_S_OUT_OF_MEMORY - As needed.
--*/
{
    NTSTATUS NtStatus;
    ULARGE_INTEGER Time;
    ULONG Range;
    ULONG Sequence;
    int Tries = 0;

    do {
        NtStatus = NtAllocateUuids(&Time, &Range, &Sequence, (char *) &Values->NodeId[0]);

        if (NtStatus == STATUS_RETRY)
            {
            WmipSleep(1);
            }

        Tries++;

        if (Tries == 20)
            {
#ifdef DEBUGRPC
            PrintToDebugger("Rpc: NtAllocateUuids retried 20 times!\n");
            ASSERT(Tries < 20);
#endif
            NtStatus = STATUS_UNSUCCESSFUL;
            }

        } while(NtStatus == STATUS_RETRY);

    if (!NT_SUCCESS(NtStatus))
        {
        return(WMI_S_OUT_OF_MEMORY);
        }

    // NtAllocateUuids keeps time in SYSTEM_TIME format which is 100ns ticks since
    // Jan 1, 1601.  UUIDs use time in 100ns ticks since Oct 15, 1582.

    // 17 Days in Oct + 30 (Nov) + 31 (Dec) + 18 years and 5 leap days.

    Time.QuadPart +=   (unsigned __int64) (1000*1000*10)       // seconds
                     * (unsigned __int64) (60 * 60 * 24)       // days
                     * (unsigned __int64) (17+30+31+365*18+5); // # of days

    ASSERT(Range);

    Values->ClockSeqHiAndReserved =
        WMI_UUID_RESERVED | (((unsigned char) (Sequence >> 8))
        & (unsigned char) WMI_UUID_CLOCK_SEQ_HI_MASK);

    Values->ClockSeqLow = (unsigned char) (Sequence & 0x00FF);

    // The order of these assignments is important

    Values->Time.QuadPart = Time.QuadPart + (Range - 1);
    Values->AllocatedCount = Range;

    /*if ((Values->NodeId[0] & 0x80) == 0)
        {*/
        return(WMI_S_OK);
        /*}
    
    return (WMI_S_UUID_LOCAL_ONLY);*/
}



WMI_STATUS WMI_ENTRY
WmipUuidCreateSequential (
    OUT UUID * Uuid
    )
/*++

Routine Description:

    This routine will create a new UUID (or GUID) which is unique in
    time and space.  We will try to guarantee that the UUID (or GUID)
    we generate is unique in time and space.  This means that this
    routine may fail if we can not generate one which we can guarantee
    is unique in time and space.

Arguments:

    Uuid - Returns the generated UUID (or GUID).

Return Value:

    WMI_S_OK - The operation completed successfully.

    RPC_S_UUID_NO_ADDRESS - We were unable to obtain the ethernet or
        token ring address for this machine.

    WMI_S_UUID_LOCAL_ONLY - On NT & Chicago if we can't get a
        network address.  This is a warning to the user, the
        UUID is still valid, it just may not be unique on other machines.

    WMI_S_OUT_OF_MEMORY - Returned as needed.
--*/
{
    WMI_UUID_GENERATE * WmiUuid = (WMI_UUID_GENERATE *) Uuid;
    WMI_STATUS Status = WMI_S_OK;
	ULARGE_INTEGER Time;
    long Delta;
    static unsigned long LastTickCount = 0;

    if (NtGetTickCount()-LastTickCount > MAX_CACHED_UUID_TIME)
        {
        UuidCachedValues.AllocatedCount = 0;
        LastTickCount = NtGetTickCount();
        }

    for(;;)
        {
        Time.QuadPart = UuidCachedValues.Time.QuadPart;

        // Copy the static info into the UUID.  We can't do this later
        // because the clock sequence could be updated by another thread.

        *(unsigned long *)&WmiUuid->ClockSeqHiAndReserved =
            *(unsigned long *)&UuidCachedValues.ClockSeqHiAndReserved;
        *(unsigned long *)&WmiUuid->NodeId[2] =
            *(unsigned long *)&UuidCachedValues.NodeId[2];

        Delta = InterlockedDecrement(&UuidCachedValues.AllocatedCount);

        if (Time.QuadPart != UuidCachedValues.Time.QuadPart)
            {
            // If our captured time doesn't match the cache then another
            // thread already took the lock and updated the cache. We'll
            // just loop and try again.
            continue;
            }

        if (Delta >= 0)
            {
            break;
            }

        //
        // Allocate block of Uuids.
        //

        Status = WmipUuidGetValues( &UuidCachedValues );
     /*   if (Status == WMI_S_OK)
            {
            UuidCacheValid = CACHE_VALID;
            }
        else
            {
            UuidCacheValid = CACHE_LOCAL_ONLY;
            }*/

        if (Status != WMI_S_OK)
            {
#ifdef DEBUGRPC
            if (Status != WMI_S_OUT_OF_MEMORY)
                PrintToDebugger("RPC: UuidGetValues returned or raised: %x\n", Status);
#endif
            ASSERT( (Status == WMI_S_OUT_OF_MEMORY) );


            return Status;
            }

        // Loop
        }


    Time.QuadPart -= Delta;

    WmiUuid->TimeLow = (unsigned long) Time.LowPart;
    WmiUuid->TimeMid = (unsigned short) (Time.HighPart & 0x0000FFFF);
    WmiUuid->TimeHiAndVersion = (unsigned short)
        (( (unsigned short)(Time.HighPart >> 16)
        & WMI_UUID_TIME_HIGH_MASK ) | WMI_UUID_VERSION);

   // ASSERT(   Status == WMI_S_OK
   //        || Status == WMI_S_UUID_LOCAL_ONLY);

 /*   if (UuidCacheValid == CACHE_LOCAL_ONLY)
        {
        return WMI_S_UUID_LOCAL_ONLY;
        }*/

    return(Status);
}


NTSTATUS
WmipUuidCreate(
    OUT UUID *Uuid
    )
{

	return (NTSTATUS)WmipUuidCreateSequential (Uuid );
	
}



ULONG WmipUnicodeToAnsi(
    LPCWSTR pszW,
    LPSTR * ppszA,
    ULONG *AnsiSizeInBytes OPTIONAL
    ){

	ANSI_STRING DestinationString;
	UNICODE_STRING SourceString;
	NTSTATUS Status;

	RtlInitAnsiString(&DestinationString,(PCHAR)ppszA);
	RtlInitUnicodeString(&SourceString,(PWSTR)pszW);
	
	Status = RtlUnicodeStringToAnsiString( &DestinationString, &SourceString, (ppszA == NULL) );

	if(ppszA != NULL ){

		memcpy(ppszA,DestinationString.Buffer,DestinationString.Length);
	}
	
	if (AnsiSizeInBytes != NULL){

        *AnsiSizeInBytes = DestinationString.Length;
    }

	return Status;
}

ULONG WmipAnsiToUnicode(
    LPCSTR pszA,
    LPWSTR * ppszW
    ){

	UNICODE_STRING DestinationString;
	ANSI_STRING SourceString;
	NTSTATUS Status;

	RtlInitUnicodeString(&DestinationString,(PWSTR)ppszW);
	RtlInitAnsiString(&SourceString,(PCHAR)pszA);
	
	Status = RtlAnsiStringToUnicodeString( &DestinationString, &SourceString, (ppszW == NULL) );

	if(ppszW != NULL ){

		memcpy(ppszW,DestinationString.Buffer,DestinationString.Length);
	}
    
	return Status;
}