/*++

Copyright (c) 1992  Microsoft Corporation

Module Name:

    Shutdown.c

Abstract:

    This module contains the server side implementation for the Win32 remote
    shutdown APIs, that is:

        - BaseInitiateSystemShutdown
        - BaseAbortSystemShutdown

Author:

    Dave Chalmers (davidc) 29-Apr-1992

Notes:


Revision History:

    19-Oct-1993     Danl
        Removed HackExtraThread which was only here to work around a bug in
        the UserSrv.  The text describing the reason for this workaround is
        as follows:
        HACKHACK - Work around bug in UserSrv that causes ExitWindowsEx to
            fail (error 5) when called by a process which doesn't
            have any threads which have called User APIs.  Remove
            after UserSrv is fixed.  See NTBUG 11601.
            Workaround is to create a thread which will make one
            API call then sleep forever.

--*/


#include "precomp.h"
#pragma hdrstop

#define RPC_NO_WINDOWS_H
#include "regrpc.h"
#include "ntrpcp.h"
#include <rpc.h>

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

//
// Shutdown Dialog Return Codes:
//

#define SHUTDOWN_SUCCESS        0
#define SHUTDOWN_USER_LOGOFF    1
#define SHUTDOWN_DESKTOP_SWITCH 2
#define SHUTDOWN_CANCELLED      3

//
// System Shutdown globals
//

RTL_CRITICAL_SECTION ShutdownCriticalSection; // Protect global shutdown data

//
// Set when a thread has a shutdown 'in progress'
// (Protected by critical section)
//

BOOL ShutdownInProgress;

//
// Set when a thread wants to interrupt the shutdown
// (Protected by critical section)
//

BOOL AbortShutdown;


//
// Data for shutdown UI - this is protected by the ShutdownInProgress flag.
// i.e. only the current shutdown thread manipulates this data.
//

LARGE_INTEGER ShutdownTime;
DWORD ShutdownDelayInSeconds;
PTCH ShutdownMessage;
DWORD ExitWindowsFlags;
DWORD GinaCode;
PTSTR UserName;
PTSTR UserDomain;
BOOL AllowLogonDuringShutdown = TRUE ;
ActiveDesktops  ShutdownDesktop;
WINDOWPLACEMENT ShutdownWindowPlacement;
BOOL ShutdownGetPlacement = FALSE;
BOOL ShutdownHasBegun = FALSE;

//
// Data captured during initialization
//

PGLOBALS GlobalWinMainData;





//
// Private prototypes
//


DWORD
InitializeShutdownData(
    PUNICODE_STRING lpMessage,
    DWORD dwTimeout,
    BOOL bForceAppsClosed,
    BOOL bRebootAfterShutdown
    );

VOID
FreeShutdownData(
    VOID
    );

BOOL WINAPI
ShutdownApiDlgProc(
    HWND    hDlg,
    UINT    message,
    WPARAM  wParam,
    LPARAM  lParam
    );

BOOL
UpdateTimeToShutdown(
    HWND    hDlg
    );

VOID
CentreWindow(
    HWND    hwnd
    );

DWORD
TestClientPrivilege(
    VOID
    );

DWORD
GetClientId(
    PTSTR *UserName,
    PTSTR *UserDomain
    );

VOID
DeleteClientId(
    PTSTR UserName,
    PTSTR UserDomain
    );

BOOL
InsertClientId(
    HWND hDlg,
    int ControlId,
    PTSTR UserName,
    PTSTR UserDomain
    );





BOOL
InitializeShutdownModule(
    PGLOBALS pGlobals
    )
/*++

Routine Description:

    Does any initializtion required for this module.

Arguments:

    pGlobals - Pointer to global data defined in WinMain

Return Value:

    Returns TRUE on success, FALSE on failure.

--*/
{
    NTSTATUS Status;

    //
    // Initialize global variables
    //

    ShutdownInProgress = FALSE;

    //
    // Initialize critical section to protect globals
    //

    Status = RtlInitializeCriticalSection(&ShutdownCriticalSection);

#if DBG
    if (!NT_SUCCESS(Status)) {
        DbgPrint("Registry Server : Shutdown : Failed to initialize critical section\n");
    }
#endif

    GlobalWinMainData = pGlobals;
    return(NT_SUCCESS(Status));
}

ULONG
BaseInitiateSystemShutdown(
    IN PREGISTRY_SERVER_NAME ServerName,
    IN PUNICODE_STRING lpMessage OPTIONAL,
    IN DWORD dwTimeout,
    IN BOOLEAN bForceAppsClosed,
    IN BOOLEAN bRebootAfterShutdown
    )
/*++

Routine Description:

    Initiates the shutdown of this machine.

Arguments:

    ServerName - Name of machine this server code is running on. (Ignored)

    lpMessage - message to display during shutdown timeout period.

    dwTimeout - number of seconds to delay before shutting down

    bForceAppsClosed - Normally applications may prevent system shutdown.
              - If this true, all applications are terminated unconditionally.

    bRebootAfterShutdown - TRUE if the system should reboot. FALSE if it should
              - be left in a shutdown state.

Return Value:

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

--*/

{
    NTSTATUS Status;
    DWORD Error;

    //
    // Check the caller has the appropriate privilege
    //

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

    //
    // Enter the critical section so we can look at our globals
    //

    Status = RtlEnterCriticalSection(&ShutdownCriticalSection);
    if (!NT_SUCCESS(Status)) {
        return(RtlNtStatusToDosError(Status));
    }

    //
    // Set up our global shutdown data.
    // Fail if a shutdown is already in progress
    //

    if (ShutdownInProgress) {
        Error = ERROR_SHUTDOWN_IN_PROGRESS;
    } else {

        //
        // Set up our globals for the shutdown thread to use.
        //

        Error = InitializeShutdownData(lpMessage,
                                       dwTimeout,
                                       bForceAppsClosed,
                                       bRebootAfterShutdown
                                      );
        if (Error == ERROR_SUCCESS) {
            ShutdownInProgress = TRUE;
            AbortShutdown = FALSE;
        }
    }

    //
    // Leave the critical section
    //

    Status = RtlLeaveCriticalSection(&ShutdownCriticalSection);
    if (Error == ERROR_SUCCESS) {
        if (!NT_SUCCESS(Status)) {
            Error = RtlNtStatusToDosError(Status);
        }
    } else {
        ASSERT(NT_SUCCESS(Status));
    }



    //
    // Create a thread to handle the shutdown (UI and calling ExitWindows)
    // The thread will handle resetting our shutdown data and globals.
    //

    if (Error == ERROR_SUCCESS) {
        int Result;

        //
        // Have winlogon create us a thread running on the user's desktop.
        //
        // The thread will do a call back to ShutdownThread()
        //

        GlobalWinMainData->LogoffFlags = EWX_WINLOGON_API_SHUTDOWN | ExitWindowsFlags;
        Result = InitiateLogoff( GlobalWinMainData,
                                 EWX_WINLOGON_API_SHUTDOWN | ExitWindowsFlags );


        if (Result != DLG_SUCCESS ) {
            Error = GetLastError();
            KdPrint(("InitiateSystemShutdown : Failed to create shutdown thread. Error = %d\n", Error));
            FreeShutdownData();
            ShutdownInProgress = FALSE; // Atomic operation
        }
    }

    return(Error);

    UNREFERENCED_PARAMETER(ServerName);
}



DWORD
InitializeShutdownData(
    PUNICODE_STRING lpMessage,
    DWORD dwTimeout,
    BOOL bForceAppsClosed,
    BOOL bRebootAfterShutdown
    )
/*++

Routine Description:

    Stores the passed shutdown parameters in our global data.

Return Value:

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

--*/

{
    NTSTATUS Status;
    LARGE_INTEGER TimeNow;
    LARGE_INTEGER Delay;
    DWORD Error;

    //
    // Set the shutdown time
    //

    ShutdownDelayInSeconds = dwTimeout;

    Status = NtQuerySystemTime(&TimeNow);
    if (!NT_SUCCESS(Status)) {
        return(RtlNtStatusToDosError(Status));
    }

    Delay = RtlEnlargedUnsignedMultiply(dwTimeout, 10000000);   // Delay in 100ns

    ShutdownTime.QuadPart = TimeNow.QuadPart + Delay.QuadPart;


    //
    // Set the shutdown flags
    //
    // We set the EWX_WINLOGON_OLD_xxx and EWX_xxx both since this message
    // originates from the winlogon process.  When these flags actually bubble
    // back to the active dialog box, winlogon expects the EWX_WINLOGON_OLD_xxx
    // to indicate the 'real' request.
    //

    ExitWindowsFlags = EWX_LOGOFF | EWX_SHUTDOWN | EWX_WINLOGON_OLD_SHUTDOWN;
    ExitWindowsFlags |= bForceAppsClosed ? EWX_FORCE : 0;
    ExitWindowsFlags |= bRebootAfterShutdown ?
                        (EWX_REBOOT | EWX_WINLOGON_OLD_REBOOT) : 0;

    if (bRebootAfterShutdown)
    {
        GinaCode = WLX_SAS_ACTION_SHUTDOWN_REBOOT;
    }
    else
    {
        GinaCode = WLX_SAS_ACTION_SHUTDOWN;
    }


    //
    // Store the caller's username and domain.
    //

    Error = GetClientId(&UserName, &UserDomain);
    if (Error != ERROR_SUCCESS) {
        return(Error);
    }


    //
    // Set the shutdown message
    //

    if (lpMessage != NULL) {

        //
        // Copy the message into a global buffer
        //

        USHORT Bytes = lpMessage->Length + (USHORT)sizeof(UNICODE_NULL);

        ShutdownMessage = (PTCH)LocalAlloc(LPTR, Bytes);
        if (ShutdownMessage == NULL) {
            DeleteClientId(UserName, UserDomain);
            return(ERROR_NOT_ENOUGH_MEMORY);
        }

        RtlMoveMemory(ShutdownMessage, lpMessage->Buffer, lpMessage->Length);
        ShutdownMessage[lpMessage->Length / sizeof(WCHAR)] = 0; // Null terminate

    } else {
        ShutdownMessage = NULL;
    }


    return(ERROR_SUCCESS);
}



VOID
FreeShutdownData(
    VOID
    )
/*++

Routine Description:

    Frees up any memory allocated to store the shutdown data

Return Value:

    None.

--*/

{
    if (ShutdownMessage != NULL) {
        LocalFree(ShutdownMessage);
        ShutdownMessage = NULL;
    }

    DeleteClientId(UserName, UserDomain);
    UserName = NULL;
    UserDomain = NULL;
}



BOOLEAN
ShutdownThread(
    VOID
    )
/*++

Routine Description:

    Handles the display of a shutdown dialog and coordinating with the
    AbortShutdown API.

Arguments:

    None

Return Value:

    TRUE - system should be shut down
    FALSE - shutdown was aborted

--*/
{
    NTSTATUS Status;
    DWORD Error;
    BOOL DoShutdown = TRUE;
    HDESK hdesk;
    BOOL CloseDesktopHandle;
    DWORD Result;
    BOOL Locked;
    BOOL Success;

    //
    // Quick check so we don't get into thorny race conditions.
    //

    if ( ShutdownDelayInSeconds == 0 )
    {

        FreeShutdownData();

        RtlEnterCriticalSection( &ShutdownCriticalSection );

        ShutdownInProgress = FALSE ;

        RtlLeaveCriticalSection( &ShutdownCriticalSection );

        GlobalWinMainData->LastGinaRet = GinaCode;

        ShutdownHasBegun = TRUE;

        return( TRUE );

    }


    hdesk = GetActiveDesktop(&GlobalWinMainData->WindowStation,
                             &CloseDesktopHandle,
                             &Locked);

    while (hdesk != NULL)
    {
        DebugLog((DEB_TRACE, "Starting shutdown dialog on desktop %x\n", hdesk));

        if (Locked)
        {
            UnlockWindowStation(GlobalWinMainData->WindowStation.hwinsta);
        }

        Success = SetThreadDesktop(hdesk);
        if (!Success)
        {
            DebugLog((DEB_TRACE, "Unable to set desktop, %d\n", GetLastError()));
        }

        if (Locked)
        {
            LockWindowStation(GlobalWinMainData->WindowStation.hwinsta);
        }

        ShutdownDesktop = GlobalWinMainData->WindowStation.ActiveDesktop;


        //
        // Push the timeout past the shutdown delay, so that we can
        // catch the messages we want, without stomping on the timeout
        // structures.
        //
        Result = DialogBoxParam( GetModuleHandle(NULL),
                                 MAKEINTRESOURCE( IDD_SYSTEM_SHUTDOWN ),
                                 NULL,
                                 ShutdownApiDlgProc,
                                 (LPARAM) 0 );

        DebugLog((DEB_TRACE, "Shutdown Dialog Returned %d\n", Result ));



        if (CloseDesktopHandle)
        {
            CloseDesktop( hdesk );
        }

        if ((Result == SHUTDOWN_SUCCESS) ||
            (Result == SHUTDOWN_CANCELLED) )
        {
            break;
        }

        //
        // Trickier ones:
        //

        if (Result == SHUTDOWN_USER_LOGOFF)
        {
            if (!AllowLogonDuringShutdown)
            {
                break;
            }

        }

        ShutdownGetPlacement = TRUE;

        hdesk = GetActiveDesktop(&GlobalWinMainData->WindowStation,
                                 &CloseDesktopHandle,
                                 &Locked);

        DebugLog((DEB_TRACE, "Switching to current desktop and restarting dialog\n"));

    }

    //
    // The shutdown has either completed or been cancelled
    // Reset our globals.
    //
    // Note we need to reset the shutdown-in-progress flag before
    // entering the non-abortable part of shutdown so that anyone
    // trying to abort from here on in will get a failure return code.
    //

    FreeShutdownData();


    Status = RtlEnterCriticalSection(&ShutdownCriticalSection);
    Error = RtlNtStatusToDosError(Status);

    if (Error == ERROR_SUCCESS) {

        //
        // Reset the global shutdown-in-progress flag
        // and check for an abort request.
        //

        if (AbortShutdown) {
            DoShutdown = FALSE;
        }

        ShutdownInProgress = FALSE;

        //
        // Leave the critical section
        //

        Status = RtlLeaveCriticalSection(&ShutdownCriticalSection);
        if (!NT_SUCCESS(Status)) {
            Error = RtlNtStatusToDosError(Status);
        }
    }

    //
    // If DoShutdown, update the last gina ret so that
    // the shutdown code will know what to do:
    //

    if ( DoShutdown )
    {
        GlobalWinMainData->LastGinaRet = GinaCode;

        ShutdownHasBegun = TRUE;
    }



    //
    // Tell the caller if he should shut down.
    //

    return DoShutdown;

}



BOOL WINAPI
ShutdownApiDlgProc(
    HWND    hDlg,
    UINT    message,
    WPARAM  wParam,
    LPARAM  lParam
    )
/*++

Routine Description:

    Processes messages for the shutdown dialog

    Dialog returns ERROR_SUCCESS if shutdown should proceed,
    ERROR_OPERATION_ABORTED if shutdown should be cancelled.

--*/
{
    HMENU hMenu;

    switch (message) {

    case WM_INITDIALOG:

        //
        // Add the caller's id to the main message text
        //

        InsertClientId(hDlg, IDD_SYSTEM_MESSAGE, UserName, UserDomain);

        //
        // Setup the client's message
        //

        SetDlgItemText(hDlg, IDD_MESSAGE, ShutdownMessage);

        //
        // Remove the close item from the system menu
        //

        hMenu = GetSystemMenu(hDlg, FALSE);
        DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);

        //
        // Position ourselves
        //

        if ( ShutdownGetPlacement )
        {
            SetWindowPlacement( hDlg, &ShutdownWindowPlacement );
        }
        else
        {
            CentreWindow(hDlg);
        }

        SetWindowPos( hDlg, HWND_TOPMOST, 0, 0, 0, 0,
                        SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW );


        //
        // Start the timer
        //

        SetTimer(hDlg, 0, 1000, NULL);  // 1 second timer

        //
        // Check if it's over before we've even started
        //

        if (UpdateTimeToShutdown(hDlg)) {

            // It's already time to shutdown
            EndDialog(hDlg, SHUTDOWN_SUCCESS);
        }

        //
        // Let everyone know what state we're in
        //

        GlobalWinMainData->PreviousWinlogonState = GlobalWinMainData->WinlogonState;
        GlobalWinMainData->WinlogonState = Winsta_InShutdownDlg;

//        GlobalWinMainData->ShutdownStarted = TRUE;

        return(TRUE);


    case WM_TIMER:

        //
        // Check for abort flag
        //

        if (AbortShutdown) {
            if (GlobalWinMainData->WinlogonState == Winsta_InShutdownDlg) {
                GlobalWinMainData->WinlogonState = GlobalWinMainData->PreviousWinlogonState;
            }
//            GlobalWinMainData->ShutdownStarted = FALSE;
            EndDialog(hDlg, SHUTDOWN_CANCELLED);
            return(TRUE);
        }

        if ( GlobalWinMainData->WindowStation.ActiveDesktop != ShutdownDesktop )
        {
            GetWindowPlacement( hDlg, &ShutdownWindowPlacement );
            EndDialog( hDlg, SHUTDOWN_DESKTOP_SWITCH );
            return( TRUE );
        }

        //
        // Update the time delay and check if our time's up
        //

        if (!UpdateTimeToShutdown(hDlg)) {

            //
            // Keep waiting
            //

            return(TRUE);
        }

        //
        // Shutdown time has arrived. Drop through...
        //

    case WLX_WM_SAS:


        DebugLog((DEB_TRACE, "Sas message received?  wParam = %d\n", wParam ));
        if ((wParam == WLX_SAS_TYPE_SCRNSVR_TIMEOUT) &&
            (message == WLX_WM_SAS)) {

           //
           // Don't end the dialog if it's just a screen saver timeout
           //

           return(TRUE);

        } else if ((wParam == WLX_SAS_TYPE_CTRL_ALT_DEL) &&
                   (message == WLX_WM_SAS)) {

           //
           // Also don't end the dialog if it's a Ctrl-Alt-Del
           //

           Sleep (1000);
           return(TRUE);

        } else {

           //
           // If the user logs off, preempt the timeout, restore the state
           //

           if (GlobalWinMainData->WinlogonState == Winsta_InShutdownDlg) {
               GlobalWinMainData->WinlogonState = GlobalWinMainData->PreviousWinlogonState;
           }

           EndDialog(hDlg, SHUTDOWN_SUCCESS);

           return(TRUE);

        }

    }

    // We didn't process this message
    return FALSE;

    UNREFERENCED_PARAMETER(lParam);
}




BOOL
UpdateTimeToShutdown(
    HWND    hDlg
    )
/*++

Routine Description:

    Updates the display of the time to system shutdown.

Returns:

    TRUE if shutdown time has arrived, otherwise FALSE

--*/
{
    NTSTATUS Status;
    BOOLEAN Success;
    LARGE_INTEGER TimeNow;
    ULONG ElapsedSecondsNow;
    ULONG ElapsedSecondsAtShutdown;
    ULONG SecondsRemaining;
    ULONG DaysRemaining;
    ULONG HoursRemaining;
    ULONG MinutesRemaining;
    TCHAR Message[40];

    //
    // Set the shutdown time
    //

    Status = NtQuerySystemTime(&TimeNow);
    ASSERT(NT_SUCCESS(Status));

    if (TimeNow.QuadPart >= ShutdownTime.QuadPart)
    {
        return(TRUE);
    }

    Success = RtlTimeToSecondsSince1980(&TimeNow, &ElapsedSecondsNow);
    ASSERT(Success);

    Success = RtlTimeToSecondsSince1980(&ShutdownTime, &ElapsedSecondsAtShutdown);
    ASSERT(Success);

    SecondsRemaining = ElapsedSecondsAtShutdown - ElapsedSecondsNow;

    //
    // Convert the seconds remaining to a string
    //

    MinutesRemaining = SecondsRemaining / 60;
    HoursRemaining = MinutesRemaining / 60;
    DaysRemaining = HoursRemaining / 24;

    SecondsRemaining = SecondsRemaining % 60;
    MinutesRemaining = MinutesRemaining % 60;
    HoursRemaining = HoursRemaining % 24;

    if (DaysRemaining > 0) {
        wsprintf(Message, TEXT("%d days"), DaysRemaining);
    } else {
        wsprintf(Message, TEXT("%02d:%02d:%02d"), HoursRemaining, MinutesRemaining, SecondsRemaining);
    }

    SetDlgItemText(hDlg, IDD_TIMER, Message);

    return(FALSE);
}



ULONG
BaseAbortSystemShutdown(
    IN PREGISTRY_SERVER_NAME ServerName
    )
/*++

Routine Description:

    Aborts a pending shutdown of this machine.

Arguments:

    ServerName - Name of machine this server code is running on. (Ignored)

Return Value:

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

--*/

{
    NTSTATUS Status;
    DWORD Error;

    //
    // Check the caller has the appropriate privilege
    //

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

    //
    // Enter the critical section so we can look at our globals
    //

    Status = RtlEnterCriticalSection(&ShutdownCriticalSection);
    if (!NT_SUCCESS(Status)) {
        return(RtlNtStatusToDosError(Status));
    }


    //
    // If a shutdown is in progress, set the abort flag
    //

    if (ShutdownInProgress) {
        AbortShutdown = TRUE;
        Error = ERROR_SUCCESS;
    } else
    {
        if ( ShutdownHasBegun )
        {
            Error = ERROR_SHUTDOWN_IN_PROGRESS;
        }
        else
        {
            Error = ERROR_NO_SHUTDOWN_IN_PROGRESS;
        }
    }

    //
    // Leave the critical section
    //

    Status = RtlLeaveCriticalSection(&ShutdownCriticalSection);
    if (Error == ERROR_SUCCESS) {
        if (!NT_SUCCESS(Status)) {
            Error = RtlNtStatusToDosError(Status);
        }
    } else {
        ASSERT(NT_SUCCESS(Status));
    }

    return(Error);

    UNREFERENCED_PARAMETER(ServerName);
}



DWORD
TestClientPrivilege(
    VOID
    )
/*++

Routine Description:

    Checks if the client has the privilege to perform the requested shutdown.

Arguments:

    None

Return Value:

    ERROR_SUCCESS if the client has the appropriate privilege.

    ERROR_ACCESS_DENIED - client does not have the required privilege

--*/
{
    NTSTATUS Status, IgnoreStatus;
    BOOL LocalConnection;
    LUID PrivilegeRequired;
    PRIVILEGE_SET PrivilegeSet;
    BOOLEAN Privileged;
    USER_SESSION_KEY SessionKey;
    HANDLE Token;

    UNICODE_STRING SubSystemName;   // LATER this should be global
    RtlInitUnicodeString(&SubSystemName, L"Win32 Registry/SystemShutdown module");

    //
    // Find out if this is a local connection
    //

    Status = RtlGetUserSessionKeyServer(NULL, &SessionKey);
    if (NT_SUCCESS(Status)) {

        LocalConnection = (Status == STATUS_LOCAL_USER_SESSION_KEY);

        if (LocalConnection) {
            PrivilegeRequired = RtlConvertLongToLuid(SE_SHUTDOWN_PRIVILEGE);
        } else {
            PrivilegeRequired = RtlConvertLongToLuid(SE_REMOTE_SHUTDOWN_PRIVILEGE);
        }


        //
        // See if the client has the required privilege
        //

        Status = I_RpcMapWin32Status(RpcImpersonateClient( NULL ));
        if (NT_SUCCESS(Status)) {

            PrivilegeSet.PrivilegeCount = 1;
            PrivilegeSet.Control = PRIVILEGE_SET_ALL_NECESSARY;
            PrivilegeSet.Privilege[0].Luid = PrivilegeRequired;
            PrivilegeSet.Privilege[0].Attributes = 0;

            Status = NtOpenThreadToken( NtCurrentThread(),
                                        TOKEN_QUERY,
                                        TRUE,
                                        &Token);
            if (NT_SUCCESS(Status)) {

                Status = NtPrivilegeCheck(Token,
                                          &PrivilegeSet,
                                          &Privileged);

                if (NT_SUCCESS(Status) || (Status == STATUS_PRIVILEGE_NOT_HELD)) {

                    Status = NtPrivilegeObjectAuditAlarm(
                                            &SubSystemName,
                                            NULL,
                                            Token,
                                            0,
                                            &PrivilegeSet,
                                            Privileged);
                }

                IgnoreStatus = NtClose(Token);
                ASSERT(NT_SUCCESS(IgnoreStatus));
            }

        }

        IgnoreStatus = I_RpcMapWin32Status(RpcRevertToSelf());
        ASSERT( NT_SUCCESS(IgnoreStatus) );
    }


    //
    // Handle unexpected errors
    //

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


    //
    // If they failed the privilege check, return an error
    //

    if (!Privileged) {
        return(ERROR_ACCESS_DENIED);
    }

    //
    // They passed muster
    //

    return(ERROR_SUCCESS);
}




DWORD
GetClientId(
    PTSTR *UserName,
    PTSTR *UserDomain
    )
/*++

Routine Description:

    Gets the name and domain of the caller, allocates and returns pointers
    to the information.

    Note we have RPC impersonate the client to discover their ID.

Arguments:

    UserName - a pointer to a NULL terminated string containing the client's
               user name is returned here.

    DomainName - a pointer to a NULL terminated string containing the client's
               domain name is returned here.

    The caller should free UserName and DomainName by calling DeleteClientId

Return Value:

    ERROR_SUCCESS - UserName and UserDomain contain valid pointers

    Other - UserName and UserDomain are invalid

--*/
{
    HANDLE  TokenHandle;
    DWORD   cbNeeded;
    PTOKEN_USER pUserToken;
    BOOL    ReturnValue=FALSE;
    DWORD   cbDomain;
    DWORD   cbName;
    SID_NAME_USE SidNameUse;
    DWORD Error;
    DWORD IgnoreError;

    //
    // Prepare for failure
    //

    *UserName = NULL;
    *UserDomain = NULL;


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

    if (OpenThreadToken(GetCurrentThread(),
                         TOKEN_QUERY,
                         FALSE,
                         &TokenHandle)) {
        //
        // Get the user Sid
        //

        if (!GetTokenInformation(TokenHandle, TokenUser,  (PVOID)NULL, 0, &cbNeeded)) {

            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

                pUserToken = (PTOKEN_USER)LocalAlloc(LPTR, cbNeeded);

                if (pUserToken != NULL) {

                    if (GetTokenInformation(TokenHandle, TokenUser,  pUserToken,
                                            cbNeeded, &cbNeeded)) {

                        //
                        // Convert User Sid to name/domain
                        //

                        cbName = 0;
                        cbDomain = 0;

                        if (!LookupAccountSid(NULL,
                                              pUserToken->User.Sid,
                                              NULL, &cbName,
                                              NULL, &cbDomain,
                                              &SidNameUse)) {

                            if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {

                                *UserDomain = (PTSTR)LocalAlloc(LPTR, cbDomain*sizeof(TCHAR));
                                *UserName = (PTSTR)LocalAlloc(LPTR, cbName*sizeof(TCHAR));

                                if ((*UserDomain != NULL) && (*UserName != NULL)) {

                                    ReturnValue = LookupAccountSid(
                                                      NULL,
                                                      pUserToken->User.Sid,
                                                      *UserName, &cbName,
                                                      *UserDomain, &cbDomain,
                                                      &SidNameUse);
                                }
                            }

                        }
                    }

                    LocalFree(pUserToken);
                }
            }
        }

        CloseHandle(TokenHandle);
    }


    IgnoreError = RpcRevertToSelf();
    ASSERT(IgnoreError == ERROR_SUCCESS);


    //
    // Clean up on failure
    //

    if (ReturnValue) {
        Error = ERROR_SUCCESS;
    } else {

        Error = GetLastError();

        DeleteClientId(*UserName, *UserDomain);

        *UserName = NULL;
        *UserDomain = NULL;
    }


    return(Error);
}




VOID
DeleteClientId(
    PTSTR UserName,
    PTSTR UserDomain
    )
/*++

Routine Description:

    Frees the client id returned previously by GetClientId

Arguments:

    UserName - a pointer to the username returned by GetClientId.

    DomainName - a pointer to the domain name eturned by GetClientId

Return Value:

    None

--*/
{
    if (UserName != NULL) {
        LocalFree(UserName);
    }

    if (UserDomain != NULL) {
        LocalFree(UserDomain);
    }

}




BOOL
InsertClientId(
    HWND hDlg,
    int ControlId,
    PTSTR UserName,
    PTSTR UserDomain
    )
/*++

Routine Description:

    Takes the text from the specified dialog control, treats it as a printf
    formatting string and inserts the user name and domain as the first 2
    string identifiers (%s)

Arguments:

    UserName - a pointer to the username returned by GetClientId.

    DomainName - a pointer to the domain name eturned by GetClientId

Return Value:

    TRUE on success, FALSE on failure

--*/
{
    DWORD   StringLength;
    DWORD   StringBytes;
    PTSTR   FormatBuffer;
    PTSTR   Buffer;

    //
    // Allocate space for the formatting string out of the control
    //

    StringLength = (DWORD)SendMessage(GetDlgItem(hDlg, ControlId), WM_GETTEXTLENGTH, 0, 0);
    StringBytes = (StringLength + 1) * sizeof(TCHAR); // Allow for terminator

    FormatBuffer = (PTSTR)LocalAlloc(LPTR, StringBytes);
    if (FormatBuffer == NULL) {
        return(FALSE);
    }

    //
    // Read the format string into the buffer
    //

    GetDlgItemText(hDlg, ControlId, FormatBuffer, StringLength);

    //
    // Calculate the maximum size of the string we'll create
    // i.e. Formatting string + username + userdomain
    //

    StringLength += lstrlen(UserName);
    StringLength += lstrlen(UserDomain);

    //
    // Allocate space for formatted string
    //

    StringBytes = (StringLength + 1) * sizeof(TCHAR); // Allow for terminator

    Buffer = (PTSTR)LocalAlloc(LPTR, StringBytes);
    if (Buffer == NULL) {
        LocalFree(FormatBuffer);
        return(FALSE);
    }

    //
    // Insert the user id into the format string
    //

    wsprintf(Buffer, FormatBuffer, UserDomain, UserName);
    ASSERT((lstrlen(Buffer) * sizeof(TCHAR)) < StringBytes);

    //
    // Replace the control text with our formatted result
    //

    SetDlgItemText(hDlg, ControlId, Buffer);

    //
    // Tidy up
    //

    LocalFree(FormatBuffer);
    LocalFree(Buffer);


    return(TRUE);
}