//*************************************************************
//
//  Profile management routines
//
//  Microsoft Confidential
//  Copyright (c) Microsoft Corporation 1995
//  All rights reserved
//
//*************************************************************

#include "uenv.h"

//
// Local function proto-types
//

BOOL CheckNetDefaultProfile (LPPROFILE lpProfile);
BOOL ParseProfilePath(LPPROFILE lpProfile, LPTSTR lpProfilePath);
BOOL RestoreUserProfile(LPPROFILE lpProfile);
BOOL TestIfUserProfileLoaded(HANDLE hUserToken, LPPROFILEINFO lpProfileInfo);
BOOL GetTempProfileDir(LPPROFILE lpProfile, LPTSTR lpProfileImage);
BOOL ComputeLocalProfileName (LPPROFILE lpProfile, LPTSTR lpUserName,
                              LPTSTR lpProfileImage, DWORD  cchMaxProfileImage,
                              LPTSTR lpExpProfileImage, DWORD  cchMaxExpProfileImage);
BOOL CreateLocalProfileKey (LPPROFILE lpProfile, PHKEY phKey, BOOL *bKeyExists);
BOOL GetLocalProfileImage(LPPROFILE lpProfile, BOOL *bNewUser);
BOOL IsCentralProfileReachable(LPPROFILE lpProfile, BOOL *bCreateCentralProfile,
                               BOOL *bMandatory);
BOOL UpdateToLatestProfile(LPPROFILE lpProfile, LPTSTR lpCentralProfile,
                           LPTSTR lpLocalProfile, LPTSTR lpSidString);
BOOL IssueDefaultProfile (LPPROFILE lpProfile, LPTSTR lpDefaultProfile,
                          LPTSTR lpLocalProfile, LPTSTR lpSidString,
                          BOOL bMandatory);
BOOL UpgradeCentralProfile (LPPROFILE lpProfile, LPTSTR lpOldProfile);
BOOL UpgradeProfile (LPPROFILE lpProfile);
BOOL IsUserAGuest(LPPROFILE lpProfile);
BOOL IsUserAnAdminMember(LPPROFILE lpProfile);
BOOL SaveProfileInfo (LPPROFILE lpProfile);
LPPROFILE LoadProfileInfo(HANDLE hToken);
BOOL CheckForSlowLink(LPPROFILE lpProfile, DWORD dwTime);
BOOL APIENTRY SlowLinkDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD GetUserPreferenceValue(HANDLE hToken);
BOOL APIENTRY ChooseProfileDlgProc (HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
DWORD ApplySecurityToRegistryTree(HKEY RootKey, PSECURITY_DESCRIPTOR pSD);

//*************************************************************
//
//  LoadUserProfile()
//
//  Purpose:    Loads the user's profile, if unable to load
//              use the cached profile or issue the default profile.
//
//  Parameters: hToken          -   User's token
//              lpProfileInfo   -   Profile Information
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/6/95      ericflo    Created
//
//*************************************************************

BOOL WINAPI LoadUserProfile (HANDLE hToken, LPPROFILEINFO lpProfileInfo)
{
    LPPROFILE lpProfile;
    BOOL bResult = FALSE;
    HANDLE hEvent = NULL;
    TCHAR szEventName[MAX_PATH];
    DWORD dwResult;
    SECURITY_DESCRIPTOR	sd;
    SECURITY_ATTRIBUTES sa;

#if DBG
    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("=========================================================")));

    DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: Entering, hToken = <0x%x>, lpProfileInfo = 0x%x"),
             hToken, lpProfileInfo));

    DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: lpProfileInfo->dwFlags = <0x%x>"),
             lpProfileInfo->dwFlags));

    if (lpProfileInfo->lpUserName) {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: lpProfileInfo->lpUserName = <%s>"),
                 lpProfileInfo->lpUserName));
    } else {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: NULL user name!")));
    }

    if (lpProfileInfo->lpProfilePath) {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: lpProfileInfo->lpProfilePath = <%s>"),
                 lpProfileInfo->lpProfilePath));
    } else {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: NULL central profile path")));
    }

    if (lpProfileInfo->lpDefaultPath) {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: lpProfileInfo->lpDefaultPath = <%s>"),
                 lpProfileInfo->lpDefaultPath));
    } else {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: NULL default profile path")));
    }

    if (lpProfileInfo->lpServerName) {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: lpProfileInfo->lpServerName = <%s>"),
                 lpProfileInfo->lpServerName));
    } else {
        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: NULL server name")));
    }

    if (lpProfileInfo->dwFlags & PI_APPLYPOLICY) {
        if (lpProfileInfo->lpPolicyPath) {
            DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: lpProfileInfo->lpPolicyPath = <%s>"),
                      lpProfileInfo->lpPolicyPath));
        } else {
            DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: NULL policy path, but PI_APPLYPOLICY is set.")));
        }
    }

#endif


    //
    //  Check Parameters
    //

    if (lpProfileInfo->dwSize != sizeof(PROFILEINFO)) {
        DebugMsg((DM_WARNING, TEXT("LoadUserProfile: lpProfileInfo->dwSize != sizeof(PROFILEINFO)")));
        SetLastError(ERROR_INVALID_PARAMETER);
        goto Exit;
    }

    if (!lpProfileInfo->lpUserName || !(*lpProfileInfo->lpUserName)) {
        DebugMsg((DM_WARNING, TEXT("LoadUserProfile: received a NULL pointer for lpUserName.")));
        SetLastError(ERROR_INVALID_PARAMETER);
        goto Exit;
    }


    //
    // Create an event to prevent multiple threads/process from trying to
    // load the profile at the same time.
    //

    wsprintf (szEventName, TEXT("userenv: %s"), lpProfileInfo->lpUserName);
    CharLower (szEventName);

    InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );

    SetSecurityDescriptorDacl (
                    &sd,
                    TRUE,                           // Dacl present
                    NULL,                           // NULL Dacl
                    FALSE                           // Not defaulted
                    );

    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;
    sa.nLength = sizeof( sa );

    hEvent = CreateEvent ( &sa, TRUE, TRUE, szEventName);

    if (!hEvent) {

        if ( GetLastError() == ERROR_INVALID_HANDLE )
        {
            hEvent = OpenEvent( EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, szEventName );
        } 

        if ( !hEvent ) 
        {
           DebugMsg((DM_WARNING, TEXT("LoadUserProfile: Failed to create event %s.  Error = %d."),
              szEventName, GetLastError()));
           goto Exit;

        }
    }


    if ((WaitForSingleObject (hEvent, INFINITE) == WAIT_FAILED)) {
        DebugMsg((DM_WARNING, TEXT("LoadUserProfile: Failed to wait on the event.  Error = %d."),
                  GetLastError()));
        goto Exit;
    }

    //
    // This will clear the event so other threads/process will have to
    // wait in the WaitForSingleObject call.
    //

    ResetEvent (hEvent);


    //
    // Check if the profile is loaded already.
    //

    if (TestIfUserProfileLoaded(hToken, lpProfileInfo)) {
        bResult = TRUE;
        goto Exit;
    }


    //
    // Allocate an internal Profile structure to work with.
    //

    lpProfile = (LPPROFILE) LocalAlloc (LPTR, sizeof(PROFILE));

    if (!lpProfile) {
        DebugMsg((DM_WARNING, TEXT("LoadUserProfile: Failed to allocate memory")));
        goto Exit;
    }


    //
    // Save the data passed in.
    //

    lpProfile->dwFlags = lpProfileInfo->dwFlags;
    lpProfile->dwUserPreference = GetUserPreferenceValue(hToken);
    lpProfile->hToken = hToken;
    lstrcpy (lpProfile->szUserName, lpProfileInfo->lpUserName);

    if (lpProfileInfo->lpDefaultPath) {
        lstrcpy (lpProfile->szDefaultProfile, lpProfileInfo->lpDefaultPath);
    }

    if (lpProfileInfo->lpServerName) {
        lstrcpy (lpProfile->szServerName, lpProfileInfo->lpServerName);
    }

    if (lpProfileInfo->dwFlags & PI_APPLYPOLICY) {
	if (lpProfileInfo->lpPolicyPath) {
            lstrcpy (lpProfile->szPolicyPath, lpProfileInfo->lpPolicyPath);        
        }
    }

    //
    // If there is a central profile, check for 3.x or 4.0 format.
    //

    if (lpProfileInfo->lpProfilePath && (*lpProfileInfo->lpProfilePath)) {

        //
        // Call ParseProfilePath to work some magic on it
        //

        if (!ParseProfilePath(lpProfile, lpProfileInfo->lpProfilePath)) {
            DebugMsg((DM_WARNING, TEXT("LoadUserProfile: ParseProfilePath returned FALSE")));
            SetLastError(ERROR_INVALID_PARAMETER);
            LocalFree (lpProfile);
            goto Exit;
        }

        //
        // The real central profile directory is...
        //

        DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: ParseProfilePath returned a directory of <%s>"),
                  lpProfile->szCentralProfile));
    }


    //
    // Load the user's profile
    //

    if (!RestoreUserProfile(lpProfile)) {
        DebugMsg((DM_WARNING, TEXT("LoadUserProfile: RestoreUserProfile returned FALSE")));
        LocalFree (lpProfile);
        goto Exit;
    }


    //
    // Set the USERPROFILE environment variable into this process's
    // environmnet.  This allows ExpandEnvironmentStrings to be used
    // in the userdiff processing.
    //

    SetEnvironmentVariable (TEXT("USERPROFILE"), lpProfile->szLocalProfile);


    //
    // Upgrade the profile if appropriate.
    //

    if (!UpgradeProfile(lpProfile)) {
        DebugMsg((DM_WARNING, TEXT("LoadUserProfile: UpgradeProfile returned FALSE")));
    }


    //
    // Apply Policy
    //

    if (lpProfile->dwFlags & PI_APPLYPOLICY) {
        if (!ApplyPolicy(lpProfile)) {
            DebugMsg((DM_WARNING, TEXT("LoadUserProfile: ApplyPolicy returned FALSE")));
        }
    }
   

    //
    // Save the outgoing parameters
    //

    lpProfileInfo->hProfile = (HANDLE) lpProfile->hKeyCurrentUser;


    //
    // Save the profile information in the registry.
    //

    SaveProfileInfo (lpProfile);


    //
    // Free the structure
    //

    LocalFree (lpProfile);


    //
    // Success!
    //

    bResult = TRUE;

Exit:

    if (hEvent) {

        //
        // This will set the event so other threads/process can continue.
        //

        SetEvent (hEvent);
        CloseHandle (hEvent);
    }


    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("LoadUserProfile: Leaving with a value of %d.  hProfile = <0x%x>"),
              bResult, lpProfileInfo->hProfile));

    DebugMsg((DM_VERBOSE, TEXT("=========================================================")));

    return bResult;
}

//*************************************************************
//
//  CheckNetDefaultProfile()
//
//  Purpose:    Checks if a network profile exists and
//              caches it locally if it is valid.
//
//  Parameters: lpProfile   -   Profile information
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:   This routine assumes we are working
//              in the user's context.
//
//  History:    Date        Author     Comment
//              9/21/95     ericflo    Created
//
//*************************************************************

BOOL CheckNetDefaultProfile (LPPROFILE lpProfile)
{
    HANDLE hFile;
    WIN32_FIND_DATA fd;
    TCHAR szBuffer[MAX_PATH];
    TCHAR szLocalDir[MAX_PATH];
    LPTSTR lpEnd;
    BOOL bRetVal = TRUE;
    BOOL bProfileReady =FALSE;
    BOOL bDeleteLocal = FALSE;
    LPTSTR lpNetPath = lpProfile->szDefaultProfile;

    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile: Entering, lpNetPath = <%s>"),
             lpNetPath));


    //
    // Expand the local profile directory
    //

    ExpandEnvironmentStrings(DEFAULT_NET_PROFILE, szLocalDir, MAX_PATH);



    //
    // See if network copy exists
    //

    hFile = FindFirstFile (lpNetPath, &fd);

    if (hFile != INVALID_HANDLE_VALUE) {


        //
        // Close the find handle
        //

        FindClose (hFile);


        //
        // We found something.  Is it a directory?
        //

        if ( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {

            DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  FindFirstFile found a file.")));
            goto CheckLocal;
        }


        //
        // Is there a ntuser.* file in this directory?
        //

        lstrcpy (szBuffer, lpNetPath);
        lpEnd = CheckSlash (szBuffer);
        lstrcpy (lpEnd, c_szNTUserStar);


        hFile = FindFirstFile (szBuffer, &fd);

        if (hFile == INVALID_HANDLE_VALUE) {
            DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  FindFirstFile found a directory, but no ntuser files.")));
            goto CheckLocal;
        }

        FindClose (hFile);


        //
        // We found a valid network profile.
        //

        DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  Found a valid network profile.")));

        if (CopyProfileDirectory (lpNetPath, szLocalDir,
                                  CPD_IGNORECOPYERRORS |
                                  CPD_COPYIFDIFFERENT |
                                  CPD_SYNCHRONIZE )) {
            bProfileReady = TRUE;
            goto Exit;
        }

        DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  Failed to copy network profile to local cache.")));

    } else {

        //
        // If the network default user profile does not
        // exist, then we want to delete it off of local
        // machines.
        //

        if (GetLastError() == ERROR_FILE_NOT_FOUND) {
            bDeleteLocal = TRUE;
        }

    }


CheckLocal:

    //
    // See if local network copy exists
    //

    hFile = FindFirstFile (szLocalDir, &fd);

    if (hFile != INVALID_HANDLE_VALUE) {


        //
        // Close the find handle
        //

        FindClose (hFile);


        //
        // We found something.  Is it a directory?
        //

        if ( !(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ) {

            DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  FindFirstFile found a file.")));
            bRetVal = FALSE;
            goto Exit;
        }


        //
        // If the network copy has been deleted, then delete this
        // local copy also and just use the normal Default User profile.
        //

        if (bDeleteLocal) {
            DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  Removing local copy of network default user profile.")));
            Delnode (szLocalDir);
            goto Exit;
        }


        //
        // Is there a ntuser.* file in this directory?
        //

        lstrcpy (szBuffer, szLocalDir);
        lpEnd = CheckSlash (szBuffer);
        lstrcpy (lpEnd, c_szNTUserStar);


        hFile = FindFirstFile (szBuffer, &fd);

        if (hFile == INVALID_HANDLE_VALUE) {
            DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  FindFirstFile found a directory, but no ntuser files.")));
            bRetVal = FALSE;
            goto Exit;
        }

        FindClose (hFile);


        //
        // We found a valid local profile.
        //

        bProfileReady = TRUE;
    }


Exit:

    //
    // If we are leaving successfully, then
    // save the local profile directory.
    //

    if (bRetVal && bProfileReady) {
        DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile: setting default profile to <%s>"), szLocalDir));

        lstrcpy (lpProfile->szDefaultProfile, szLocalDir);

    } else {
        DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile: setting default profile to NULL")));

        *lpProfile->szDefaultProfile = TEXT('\0');
    }


    //
    // Tag the internal flags so we don't do this again.
    //

    lpProfile->dwInternalFlags |= DEFAULT_NET_READY;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("CheckNetDefaultProfile:  Leaving with a value of %d."), bRetVal));


    return bRetVal;

}


//*************************************************************
//
//  ParseProfilePath()
//
//  Purpose:    Parses the profile path to determine if
//              it points at a directory or a filename.
//              If the path points to a filename, a subdirectory
//              of a similar name is created.
//
//  Parameters: lpProfile       -   Profile Information
//              lpProfilePath   -   Input path
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/6/95      ericflo    Created
//
//*************************************************************

BOOL ParseProfilePath(LPPROFILE lpProfile, LPTSTR lpProfilePath)
{
    WIN32_FIND_DATA fd;
    HANDLE hResult;
    DWORD  dwError;
    TCHAR  szProfilePath[MAX_PATH];
    TCHAR  szExt[5];
    LPTSTR lpEnd;
    UINT   uiExtCount;
    BOOL   bRetVal = FALSE;
    BOOL   bUpgradeCentral = FALSE;
    BOOL   bMandatory = FALSE;
    DWORD  dwStart, dwDelta;


    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Entering, lpProfilePath = <%s>"),
             lpProfilePath));


    //
    // Impersonate the user
    //

    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("ParseProfilePath: Failed to impersonate user")));
        return FALSE;
    }

    //
    // Start by calling FindFirstFile so we have file attributes
    // to work with.
    //

    dwStart = GetTickCount();

    hResult = FindFirstFile(lpProfilePath, &fd);

    dwDelta = GetTickCount() - dwStart;

    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Tick Count = %d"), dwDelta));

    //
    // It's magic time...
    //

    if (hResult != INVALID_HANDLE_VALUE) {

        //
        // FindFirst File found something.
        // First close the handle, then look at
        // the file attributes.
        //

        DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: FindFirstFile found something with attributes <0x%x>"),
                 fd.dwFileAttributes));

        FindClose(hResult);


        //
        // If we found a directory, copy the path to
        // the result buffer and exit.
        //

        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Found a directory")));
            CheckForSlowLink (lpProfile, dwDelta);
            lstrcpy (lpProfile->szCentralProfile, lpProfilePath);
            bRetVal = TRUE;
            goto Exit;
        }


        //
        // We found a file.
        // Jump to the filename generation code.
        //

        DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Found a file")));


        //
        // We set this flag now, but it is only used if a new
        // central directory is created.
        //

        bUpgradeCentral = TRUE;

        goto GenerateDirectoryName;
    }

    //
    // FindFirstFile failed.  Look at the error to determine why.
    //

    dwError = GetLastError();
    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: FindFirstFile failed with error %d"),
              dwError));


    if ( (dwError == ERROR_FILE_NOT_FOUND) ||
         (dwError == ERROR_PATH_NOT_FOUND) ) {

        DWORD dwStrLen;

        //
        // Nothing found with this name.  If the name
        // does not end in .usr or .man, attempt to create
        // the directory.
        //

        dwStrLen = lstrlen (lpProfilePath);

        if (dwStrLen >= 4) {

            lpEnd = lpProfilePath + dwStrLen - 4;

            if ( (lstrcmpi(lpEnd, c_szUSR) != 0) &&
                 (lstrcmpi(lpEnd, c_szMAN) != 0) ) {


                if (CreateSecureDirectory(lpProfile, lpProfilePath, NULL)) {

                    //
                    // Successfully created the directory.
                    //

                    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Succesfully created the sub-directory")));
                    CheckForSlowLink (lpProfile, dwDelta);
                    lstrcpy (lpProfile->szCentralProfile, lpProfilePath);
                    bRetVal = TRUE;
                    goto Exit;

                } else {

                    //
                    // Failed to create the subdirectory
                    //

                    DebugMsg((DM_WARNING, TEXT("ParseProfilePath: Failed to create user sub-directory.  Error = %d"),
                             GetLastError()));
                }
            }
        }
    }

    else if (dwError == ERROR_ACCESS_DENIED) {
        DebugMsg((DM_WARNING, TEXT("ParseProfilePath: You don't have permission to your central profile server!  Error = %d"),
                 dwError));

        if ((lpProfile->dwUserPreference != USERINFO_LOCAL) &&
            !(lpProfile->dwInternalFlags & PROFILE_SLOW_LINK)) {

            ReportError(lpProfile->dwFlags, IDS_ACCESSDENIED, lpProfilePath);
        }
        goto DisableAndExit;
    }

    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Did not create the sub-directory.  Generating a new name.")));


GenerateDirectoryName:

    //
    // If we made it here, either:
    //
    // 1) a file exists with the same name
    // 2) the directory couldn't be created
    // 3) the profile path ends in .usr or .man
    //
    // Make a local copy of the path so we can munge it.
    //

    lstrcpy (szProfilePath, lpProfilePath);

    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Entering name generating code working with a path of <%s>."),
              szProfilePath));


    //
    // Does this path have a filename extension?
    //

    lpEnd = szProfilePath + lstrlen (szProfilePath) - 1;

    while (*lpEnd && (lpEnd >= szProfilePath)) {
        if (*lpEnd == TEXT('.'))
            break;

        if (*lpEnd == TEXT('\\'))
            break;

        lpEnd--;
    }

    if (*lpEnd != TEXT('.')) {

        //
        // The path does not have an extension.  Append .pds
        //

        lpEnd = szProfilePath + lstrlen (szProfilePath);
        lstrcpy (lpEnd, c_szPDS);

    } else {

        //
        // The path has an extension.  Append the new
        // directory extension (.pds or .pdm).
        //

        if (lstrcmpi(lpEnd, c_szMAN) == 0) {
            lstrcpy (lpEnd, c_szPDM);
            bMandatory = TRUE;

        } else {
            lstrcpy (lpEnd, c_szPDS);
        }

    }



    //
    // Call FindFirstFile to see if this directory exists.
    //

    hResult = FindFirstFile(szProfilePath, &fd);



    if (hResult != INVALID_HANDLE_VALUE) {

        //
        // FindFirst File found something.
        // First close the handle, then look at
        // the file attributes.
        //

        DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: FindFirstFile(2) found something with attributes <0x%x>"),
                 fd.dwFileAttributes));

        FindClose(hResult);


        //
        // If we found a directory, copy the path to
        // the result buffer and exit.
        //

        if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Found a directory")));
            if (bMandatory) {
                lstrcpy (lpProfile->szCentralProfile, szProfilePath);
                lpProfile->dwInternalFlags |= PROFILE_MANDATORY;
            } else {
                CheckForSlowLink (lpProfile, dwDelta);
                lstrcpy (lpProfile->szCentralProfile, szProfilePath);
            }
            bRetVal = TRUE;
            goto Exit;
        }


        //
        // We found a file that matches the generated name.
        //

        DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Found a file with the name we generated.")));

        if (!bMandatory) {
            CheckForSlowLink (lpProfile, dwDelta);
        }

        if (bMandatory || ((lpProfile->dwUserPreference != USERINFO_LOCAL) &&
            !(lpProfile->dwInternalFlags & PROFILE_SLOW_LINK))) {

            ReportError(lpProfile->dwFlags, IDS_FAILEDDIRCREATE, szProfilePath);
        }

        goto DisableAndExit;
    }


    //
    // FindFirstFile failed.  Look at the error to determine why.
    //

    dwError = GetLastError();
    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: FindFirstFile failed with error %d"),
              dwError));


    //
    // If we are working with a mandatory profile,
    // disable the central profile and try to log
    // on with a cache.
    //

    if (bMandatory) {

        DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Central mandatory profile is unreachable to due error %d."), dwError));

        if (IsUserAnAdminMember(lpProfile)) {
            ReportError(lpProfile->dwFlags, IDS_MANDATORY_NOT_AVAILABLE, dwError);

            lpProfile->szCentralProfile[0] = TEXT('\0');
            lpProfile->dwInternalFlags |= PROFILE_MANDATORY;
            bRetVal = TRUE;
        } else {
            ReportError(lpProfile->dwFlags, IDS_MANDATORY_NOT_AVAILABLE2, dwError);
        }
        goto Exit;
    }


    //
    // The user has a roaming profile, so it is ok to call
    // CheckForSlowLink now.
    //

    CheckForSlowLink (lpProfile, dwDelta);

    if ( (dwError == ERROR_FILE_NOT_FOUND) ||
         (dwError == ERROR_PATH_NOT_FOUND) ) {

        //
        // Attempt to create the directory.
        //

        if (CreateSecureDirectory(lpProfile, szProfilePath, NULL)) {

            //
            // Successfully created the directory.
            //

            DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Succesfully created the sub-directory")));

            lstrcpy (lpProfile->szCentralProfile, szProfilePath);


            if (bUpgradeCentral) {
                bRetVal = UpgradeCentralProfile (lpProfile, lpProfilePath);

            } else {
                bRetVal = TRUE;
            }

            if (bRetVal) {

                //
                // Success
                //

                CheckForSlowLink (lpProfile, dwDelta);

                goto Exit;

            } else {

                //
                // Delete the directory we created above.
                //

                Delnode (lpProfile->szCentralProfile);
            }


        } else {

            DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Failed to create the sub-directory")));

            if ((lpProfile->dwUserPreference != USERINFO_LOCAL) &&
                !(lpProfile->dwInternalFlags & PROFILE_SLOW_LINK)) {

                ReportError(lpProfile->dwFlags, IDS_FAILEDDIRCREATE2, szProfilePath, GetLastError());
            }
            goto DisableAndExit;
        }
    }

    //
    // The central profile isn't reachable, or failed to upgrade.
    // Disable the central profile and try to log
    // on with a cache.
    //

    dwError = GetLastError();
    DebugMsg((DM_VERBOSE, TEXT("ParseProfilePath: Central profile is unreachable to due error %d, switching to local profile only."), dwError));

    if ((lpProfile->dwUserPreference != USERINFO_LOCAL) &&
        !(lpProfile->dwInternalFlags & PROFILE_SLOW_LINK)) {

        ReportError(lpProfile->dwFlags, IDS_CENTRAL_NOT_AVAILABLE2, dwError);
    }

DisableAndExit:

    lpProfile->szCentralProfile[0] = TEXT('\0');

    bRetVal = TRUE;


Exit:

    //
    // Revert to being 'ourself'
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("ParseProfilePath: Failed to revert to self")));
    }

    return bRetVal;
}

//*************************************************************
//
//  RestoreUserProfile()
//
//  Purpose:    Downloads the user's profile if possible,
//              otherwise use either cached profile or
//              default profile.
//
//  Parameters: lpProfile   -   Profile information
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/19/95     ericflo    Created
//
//*************************************************************

BOOL RestoreUserProfile(LPPROFILE lpProfile)
{
    BOOL  IsCentralReachable = FALSE;
    BOOL  IsLocalReachable = FALSE;
    BOOL  IsMandatory = FALSE;
    BOOL  IsProfilePathNULL = FALSE;
    BOOL  bCreateCentralProfile = FALSE;
    BOOL  bDefaultUsed = FALSE;
    LPTSTR lpCentralProfile;
    LPTSTR lpLocalProfile;
    BOOL  bProfileLoaded = FALSE;
    BOOL bNewUser = TRUE;
    LPTSTR SidString;
    LONG error = ERROR_SUCCESS;
    TCHAR szProfile[MAX_PATH];
    LPTSTR lpEnd;
    BOOL bRet;


    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Entering")));


    //
    // Get the Sid string for the current user
    //

    SidString = GetSidString(lpProfile->hToken);
    if (!SidString) {
        DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  Failed to get sid string for user")));
        return FALSE;
    }


    //
    // Initialization
    //

    lpCentralProfile = lpProfile->szCentralProfile;


    DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Profile path = <%s>"), lpCentralProfile ? lpCentralProfile : TEXT("")));
    if (!lpCentralProfile || !(*lpCentralProfile)) {
        IsProfilePathNULL = TRUE;
    }


    //
    // Test if this user is a guest.
    //

    if (IsUserAGuest(lpProfile)) {
        lpProfile->dwInternalFlags |= PROFILE_GUEST_USER;
        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  User is a Guest")));
    }

    //
    // Test if this user is an admin.
    //

    if (IsUserAnAdminMember(lpProfile)) {
        lpProfile->dwInternalFlags |= PROFILE_ADMIN_USER;
        lpProfile->dwInternalFlags &= ~PROFILE_GUEST_USER;
        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  User is a Admin")));
    }


    //
    // Decide if the central profilemage is available.
    //

    IsCentralReachable = IsCentralProfileReachable(lpProfile,
                                                   &bCreateCentralProfile,
                                                   &IsMandatory);

    if (IsCentralReachable) {

        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Central Profile is reachable")));

        if (IsMandatory) {
            lpProfile->dwInternalFlags |= PROFILE_MANDATORY;
            DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Central Profile is mandatory")));

        } else {
            lpProfile->dwInternalFlags |= PROFILE_UPDATE_CENTRAL;
            lpProfile->dwInternalFlags |= bCreateCentralProfile ? PROFILE_NEW_CENTRAL : 0;
            DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Central Profile is floating")));

            if ((lpProfile->dwUserPreference == USERINFO_LOCAL) ||
                (lpProfile->dwInternalFlags & PROFILE_SLOW_LINK)) {
                DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Ignoring central profile due to User Preference of Local only (or slow link).")));
                IsProfilePathNULL = TRUE;
                IsCentralReachable = FALSE;
            }
        }

    } else {
        if (!IsProfilePathNULL) {
            error = GetLastError();
            DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile: IsCentralProfileReachable returned FALSE. error = %d"),
                     error));

            ReportError(lpProfile->dwFlags, IDS_CENTRAL_NOT_AVAILABLE, error);
        }
    }


    //
    // Determine if the local copy of the profilemage is available.
    //

    IsLocalReachable = GetLocalProfileImage(lpProfile, &bNewUser);

    if (IsLocalReachable) {
        lpProfile->dwInternalFlags |= PROFILE_USE_CACHE;
        lpProfile->dwInternalFlags |= bNewUser ? PROFILE_NEW_LOCAL : 0;

    } else {
        if (!GetTempProfileDir(lpProfile, lpProfile->szLocalProfile)) {
            error = GetLastError();
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  GetTempProfileDir failed with error %d.  Unable to issue temporary profile!"), error));
            ReportError(lpProfile->dwFlags, IDS_TEMP_DIR_FAILED, lpProfile->szLocalProfile, error);
            goto Exit;
        }
    }


    lpLocalProfile = lpProfile->szLocalProfile;


    DebugMsg((DM_VERBOSE, TEXT("Local profile %s reachable"), IsLocalReachable ? TEXT("is") : TEXT("is not")));
    DebugMsg((DM_VERBOSE, TEXT("Local profile name is <%s>"), lpLocalProfile));


    //
    // We can do a couple of quick checks here to filter out
    // new users.
    //

    if (( (lpProfile->dwInternalFlags & PROFILE_NEW_CENTRAL) &&
          (lpProfile->dwInternalFlags & PROFILE_NEW_LOCAL) ) ||
          (!IsCentralReachable &&
          (lpProfile->dwInternalFlags & PROFILE_NEW_LOCAL) )) {

       DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Working with a new user.  Go straight to issuing a default profile.")));
       goto IssueDefault;
    }


    //
    // If both central and local profileimages exist, reconcile them
    // and load.
    //

    if (IsCentralReachable && IsLocalReachable) {

        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  About to call UpdateToLatestProfile")));
        bRet = UpdateToLatestProfile(lpProfile, lpCentralProfile, lpLocalProfile, SidString);

        if (!bRet) {
            DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  UpdatetoLatestProfile failed.  Issuing default profile")));
            lpProfile->dwInternalFlags &= ~PROFILE_UPDATE_CENTRAL;
            lpProfile->dwInternalFlags |= PROFILE_DELETE_CACHE;
            goto IssueDefault;
        }

        lstrcpy (szProfile, lpLocalProfile);
        lpEnd = CheckSlash(szProfile);

        if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
            lstrcpy (lpEnd, c_szNTUserMan);
        } else {
            lstrcpy (lpEnd, c_szNTUserDat);
        }

        error = MyRegLoadKey(lpProfile, HKEY_USERS, SidString, szProfile);
        bProfileLoaded = (error == ERROR_SUCCESS);


        //
        // If we failed to load the central profile for some
        // reason, don't update it when we log off.
        //

        if (bProfileLoaded) {
            goto Exit;

        } else {
            lpProfile->dwInternalFlags &= ~PROFILE_UPDATE_CENTRAL;
            lpProfile->dwInternalFlags |= PROFILE_DELETE_CACHE;
            ReportError(lpProfile->dwFlags, IDS_FAILED_LOAD_LOCAL, error);
            goto IssueDefault;
        }
    }


    //
    // Only a local profile exists so use it.
    //

    if (!IsCentralReachable && IsLocalReachable) {

        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  No central profile.  Attempting to load local profile.")));

        lstrcpy (szProfile, lpLocalProfile);
        lpEnd = CheckSlash(szProfile);

        if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
            lstrcpy (lpEnd, c_szNTUserMan);
        } else {
            lstrcpy (lpEnd, c_szNTUserDat);
        }

        error = MyRegLoadKey(lpProfile, HKEY_USERS, SidString, szProfile);
        bProfileLoaded = (error == ERROR_SUCCESS);

        if (!bProfileLoaded) {

            DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  MyRegLoadKey returned FALSE.")));

            ReportError(lpProfile->dwFlags, IDS_FAILED_LOAD_LOCAL, error);
        }

        if (!bProfileLoaded && IsProfilePathNULL) {
            DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Failed to load local profile and profile path is NULL, going to overwrite local profile")));
            lpProfile->dwInternalFlags |= PROFILE_DELETE_CACHE;
            goto IssueDefault;
        }
        goto Exit;
    }


    //
    // Last combination.  Unable to access a local profile cache,
    // but a central profile exists.  Use the temporary profile.
    //


    if (IsCentralReachable) {

        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Using temporary cache with central profile")));


        //
        // Impersonate the user
        //


        if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
            DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to impersonate user")));
            goto Exit;
        }



        bRet = CopyProfileDirectory (lpCentralProfile,
                                     lpLocalProfile,
                                     CPD_IGNORECOPYERRORS |
                                     CPD_COPYIFDIFFERENT  |
                                     CPD_SYNCHRONIZE);


        //
        // Revert to being 'ourself'
        //

        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to revert to self")));
        }


        //
        // Check return value
        //

        if (!bRet) {
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  CopyProfileDirectory returned FALSE.  Error = %d"), GetLastError()));
            goto Exit;
        }

        lstrcpy (szProfile, lpLocalProfile);
        lpEnd = CheckSlash(szProfile);

        if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
            lstrcpy (lpEnd, c_szNTUserMan);
        } else {
            lstrcpy (lpEnd, c_szNTUserDat);
        }

        error = MyRegLoadKey(lpProfile,HKEY_USERS,
                                       SidString,
                                       szProfile);

        bProfileLoaded = (error == ERROR_SUCCESS);


        if (bProfileLoaded) {
            goto Exit;
        }

        //
        // If a temporary cache exists, delete it, since we will
        // generate a new one below.
        //

        if (!(lpProfile->dwInternalFlags & PROFILE_USE_CACHE)) {

            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  Deleting temporary profile directory <%s>."), lpLocalProfile));

            if (!DeleteProfile (NULL, lpLocalProfile, TRUE)) {
                DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  DeleteProfile returned false.  Error = %d"), GetLastError()));
            }
        }
    }


IssueDefault:

    DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Issuing default profile")));

    //
    // If a cache exists, delete it, since we will
    // generate a new one below.
    //

    if (lpProfile->dwInternalFlags & PROFILE_DELETE_CACHE) {

        DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  Deleting cached profile directory <%s>."), lpLocalProfile));

        lpProfile->dwInternalFlags &= ~PROFILE_DELETE_CACHE;

        if (!DeleteProfile (SidString, lpLocalProfile, TRUE)) {
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  DeleteProfileDirectory returned false.  Error = %d"), GetLastError()));
        }
    }


    //
    // Create a local profile to work with
    //

    if (!(lpProfile->dwInternalFlags & PROFILE_NEW_LOCAL)) {

        if (lpProfile->dwInternalFlags & PROFILE_USE_CACHE) {

            if (!GetLocalProfileImage(lpProfile, &bNewUser)) {
                DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  GetLocalProfileImage failed.")));
                ReportError(lpProfile->dwFlags, IDS_TEMP_DIR_FAILED, GetLastError());
                goto Exit;
            }

        } else {

            lstrcpy (szProfile, CONFIG_FILE_PATH);
            if (!ComputeLocalProfileName(lpProfile, lpProfile->szUserName,
                                        szProfile, MAX_PATH,
                                        lpLocalProfile, MAX_PATH)) {

                DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  ComputeLocalProfileName failed.")));
                goto Exit;
            }
        }
    }


    //
    // If a default profile location was specified, try
    // that first.
    //

    if ( !(lpProfile->dwInternalFlags & DEFAULT_NET_READY) ) {

        //
        // Impersonate the user
        //

        if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile: Failed to impersonate user")));
            goto IssueLocalDefault;
        }


        CheckNetDefaultProfile (lpProfile);


        //
        // Go back to system security context
        //

        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile: Failed to revert to self")));
        }
    }


    if ( lpProfile->szDefaultProfile && *lpProfile->szDefaultProfile) {

          if (IssueDefaultProfile (lpProfile, lpProfile->szDefaultProfile,
                                    lpLocalProfile, SidString,
                                    (lpProfile->dwInternalFlags & PROFILE_MANDATORY))) {

              DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Successfully setup the specified default.")));
              bProfileLoaded = TRUE;
              goto IssueDefaultExit;
          }

          DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  IssueDefaultProfile failed with specified default.")));
    }

IssueLocalDefault:

    //
    // Issue the local default profile.
    //

    if (IssueDefaultProfile (lpProfile, DEFAULT_PROFILE,
                              lpLocalProfile, SidString,
                              (lpProfile->dwInternalFlags & PROFILE_MANDATORY))) {

        DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Successfully setup the local default.")));
        bProfileLoaded = TRUE;
        goto IssueDefaultExit;
    }

    DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  IssueDefaultProfile failed with local default.")));


IssueDefaultExit:

    //
    // If the default profile was successfully issued, then
    // we need to set the security on the hive.
    //

    if (bProfileLoaded) {
        if (!SetupNewHive(lpProfile, SidString, NULL)) {
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  SetupNewHive failed")));
            bProfileLoaded = FALSE;
        }


    }


Exit:

    //
    // If the profile was loaded, then save the profile type in the
    // user's hive, and setup the "User Shell Folders" section for
    // Explorer.
    //

    if (bProfileLoaded) {

        //
        // Open the Current User key.  This will be closed in
        // UnloadUserProfile.
        //

        error = RegOpenKeyEx(HKEY_USERS, SidString, 0, KEY_ALL_ACCESS,
                             &lpProfile->hKeyCurrentUser);

        if (error != ERROR_SUCCESS) {
            bProfileLoaded = FALSE;
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  Failed to open current user key. Error = %d"), error));
        }
    }


    if (!bProfileLoaded) {

        //
        // If the user is an Admin, then let him/her log on with
        // either the .default profile, or an empty profile.
        //

        if (lpProfile->dwInternalFlags & PROFILE_ADMIN_USER) {
            ReportError(lpProfile->dwFlags, IDS_ADMIN_OVERRIDE, GetLastError());
            bProfileLoaded = TRUE;
        } else {
            DebugMsg((DM_WARNING, TEXT("RestoreUserProfile:  Failed to save settings in user hive or failed to apply security. Error = %d"), GetLastError()));
            ReportError(lpProfile->dwFlags, IDS_FAILED_LOAD_PROFILE, GetLastError());

            if (lpProfile->hKeyCurrentUser) {
                RegCloseKey (lpProfile->hKeyCurrentUser);
            }

            MyRegUnLoadKey(HKEY_USERS, SidString);
        }
    }


    DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  About to Leave.  Final Information follows:")));
    DebugMsg((DM_VERBOSE, TEXT("Profile was %s loaded."), bProfileLoaded ? TEXT("successfully") : TEXT("NOT successfully")));
    DebugMsg((DM_VERBOSE, TEXT("lpProfile->szCentralProfile = <%s>"), lpProfile->szCentralProfile));
    DebugMsg((DM_VERBOSE, TEXT("lpProfile->szLocalProfile = <%s>"), lpProfile->szLocalProfile));
    DebugMsg((DM_VERBOSE, TEXT("lpProfile->dwInternalFlags = 0x%x"), lpProfile->dwInternalFlags));


    //
    // Free up the user's sid string
    //

    DeleteSidString(SidString);

    DebugMsg((DM_VERBOSE, TEXT("RestoreUserProfile:  Leaving.")));

    return bProfileLoaded;
}


//*************************************************************
//
//  TestIfUserProfileLoaded()
//
//  Purpose:    Test to see if this user's profile is loaded.
//
//  Parameters: hToken          -   user's token
//              lpProfileInfo   -   Profile information from app
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/19/95     ericflo    Ported
//
//*************************************************************

BOOL TestIfUserProfileLoaded(HANDLE hToken, LPPROFILEINFO lpProfileInfo)
{
    LPTSTR SidString;
    DWORD error;
    HKEY hSubKey;


    //
    // Get the Sid string for the user
    //

    SidString = GetSidString(hToken);
    if (!SidString) {
        DebugMsg((DM_WARNING, TEXT("TestIfUserProfileLoaded:  Failed to get sid string for user")));
        return FALSE;
    }



    error = RegOpenKeyEx(HKEY_USERS, SidString, 0, KEY_ALL_ACCESS, &hSubKey);

    if (error == ERROR_SUCCESS) {

        DebugMsg((DM_VERBOSE, TEXT("TestIfUserProfileLoaded:  Profile already loaded.")));

        //
        // This key will be closed in UnloadUserProfile
        //

        lpProfileInfo->hProfile = (HANDLE) hSubKey;
    }

    DeleteSidString(SidString);

    return(error == ERROR_SUCCESS);
}

//*************************************************************
//
//  SecureUserKey()
//
//  Purpose:    Sets security on a key in the user's hive
//              so only admin's can change it.
//
//  Parameters: lpProfile       -   Profile Information
//              lpKey           -   Key to secure
//              pSid            -   Sid (used by CreateNewUser)
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/20/95     ericflo    Created
//
//*************************************************************

BOOL SecureUserKey(LPPROFILE lpProfile, LPTSTR lpKey, PSID pSid)
{
    DWORD Error, IgnoreError;
    HKEY RootKey;
    SECURITY_DESCRIPTOR sd;
    SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
    PACL pAcl = NULL;
    PSID  psidUser = NULL, psidSystem = NULL, psidAdmin = NULL;
    DWORD cbAcl, AceIndex, dwDisp;
    ACE_HEADER * lpAceHeader;
    BOOL bRetVal = FALSE;
    BOOL bFreeSid = TRUE;
    DWORD dwFlags = 0;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("SecureUserKey:  Entering")));


    //
    // Create the security descriptor
    //

    //
    // Give the user access by their real sid so they still have access
    // when they logoff and logon again
    //

    if (pSid) {
        psidUser = pSid;
        bFreeSid = FALSE;
        dwFlags = PI_NOUI;
    } else {
        psidUser = GetUserSid(lpProfile->hToken);
        dwFlags = lpProfile->dwFlags;
    }

    if (!psidUser) {
        DebugMsg((DM_WARNING, TEXT("SecureUserKey:  Failed to get user sid")));
        return FALSE;
    }



    //
    // Get the system sid
    //

    if (!AllocateAndInitializeSid(&authNT, 1, SECURITY_LOCAL_SYSTEM_RID,
                                  0, 0, 0, 0, 0, 0, 0, &psidSystem)) {
         DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to initialize system sid.  Error = %d"), GetLastError()));
         goto Exit;
    }


    //
    // Get the admin sid
    //

    if (!AllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
                                  DOMAIN_ALIAS_RID_ADMINS, 0, 0,
                                  0, 0, 0, 0, &psidAdmin)) {
         DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to initialize admin sid.  Error = %d"), GetLastError()));
         goto Exit;
    }


    //
    // Allocate space for the ACL
    //

    cbAcl = (2 * GetLengthSid (psidUser)) + (2 * GetLengthSid (psidSystem)) +
            (2 * GetLengthSid (psidAdmin)) + sizeof(ACL) +
            (6 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));


    pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl);
    if (!pAcl) {
        goto Exit;
    }


    if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to initialize acl.  Error = %d"), GetLastError()));
        goto Exit;
    }



    //
    // Add Aces for User, System, and Admin.  Non-inheritable ACEs first
    //

    AceIndex = 0;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, KEY_READ, psidUser)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to add ace for user.  Error = %d"), GetLastError()));
        goto Exit;
    }


    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, KEY_ALL_ACCESS, psidSystem)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to add ace for system.  Error = %d"), GetLastError()));
        goto Exit;
    }

    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, KEY_ALL_ACCESS, psidAdmin)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to add ace for admin.  Error = %d"), GetLastError()));
        goto Exit;
    }


    //
    // Now the inheritable ACEs
    //

    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_READ, psidUser)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to add ace for user.  Error = %d"), GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, AceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to get ace (%d).  Error = %d"), AceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidSystem)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to add ace for system.  Error = %d"), GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, AceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to get ace (%d).  Error = %d"), AceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidAdmin)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to add ace for admin.  Error = %d"), GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, AceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to get ace (%d).  Error = %d"), AceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    //
    // Put together the security descriptor
    //

    if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to initialize security descriptor.  Error = %d"), GetLastError()));
        goto Exit;
    }


    if (!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
        DebugMsg((DM_VERBOSE, TEXT("SecureUserKey: Failed to set security descriptor dacl.  Error = %d"), GetLastError()));
        goto Exit;
    }


    //
    // Open the root of the user's profile
    //

    Error = RegCreateKeyEx(HKEY_USERS,
                         lpKey,
                         0,
                         NULL,
                         REG_OPTION_NON_VOLATILE,
                         WRITE_DAC | KEY_ENUMERATE_SUB_KEYS | READ_CONTROL,
                         NULL,
                         &RootKey,
                         &dwDisp);

    if (Error != ERROR_SUCCESS) {

        DebugMsg((DM_WARNING, TEXT("SecureUserKey: Failed to open root of user registry, error = %d"), Error));

    } else {

        //
        // Set the security descriptor on the key
        //

        Error = ApplySecurityToRegistryTree(RootKey, &sd);


        if (Error == ERROR_SUCCESS) {
            bRetVal = TRUE;

        } else {

            DebugMsg((DM_WARNING, TEXT("SecureUserKey:  Failed to apply security to registry key, error = %d"), Error));
        }

        RegCloseKey(RootKey);
    }


Exit:

    //
    // Free the sids and acl
    //

    if (bFreeSid && psidUser) {
        DeleteUserSid (psidUser);
    }

    if (psidSystem) {
        FreeSid(psidSystem);
    }

    if (psidAdmin) {
        FreeSid(psidAdmin);
    }

    if (pAcl) {
        GlobalFree (pAcl);
    }


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("SecureUserKey:  Leaving with a return value of %d"), bRetVal));


    return(bRetVal);

}



//end
//*************************************************************
//
//  ApplySecurityToRegistryTree()
//
//  Purpose:    Applies the passed security descriptor to the passed
//              key and all its descendants.  Only the parts of
//              the descriptor inddicated in the security
//              info value are actually applied to each registry key.
//
//  Parameters: RootKey   -     Registry key
//              pSD       -     Security Descriptor
//
//  Return:     ERROR_SUCCESS if successful
//
//  Comments:
//
//  History:    Date        Author     Comment
//              7/19/95     ericflo    Created
//
//*************************************************************

DWORD ApplySecurityToRegistryTree(HKEY RootKey, PSECURITY_DESCRIPTOR pSD)

{
    DWORD Error, IgnoreError;
    DWORD SubKeyIndex;
    LPTSTR SubKeyName;
    HKEY SubKey;
    DWORD cchSubKeySize = MAX_PATH + 1;



    //
    // First apply security
    //

    Error = RegSetKeySecurity(RootKey, DACL_SECURITY_INFORMATION, pSD);

    if (Error != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("ApplySecurityToRegistryTree:  Failed to set security on registry key, error = %d"), Error));

        return Error;
    }


    //
    // Open each sub-key and apply security to its sub-tree
    //

    SubKeyIndex = 0;

    SubKeyName = GlobalAlloc (GPTR, cchSubKeySize * sizeof(TCHAR));

    if (!SubKeyName) {
        DebugMsg((DM_WARNING, TEXT("ApplySecurityToRegistryTree:  Failed to allocate memory, error = %d"), GetLastError()));
        return GetLastError();
    }

    while (TRUE) {

        //
        // Get the next sub-key name
        //

        Error = RegEnumKey(RootKey, SubKeyIndex, SubKeyName, cchSubKeySize);


        if (Error != ERROR_SUCCESS) {

            if (Error == ERROR_NO_MORE_ITEMS) {

                //
                // Successful end of enumeration
                //

                Error = ERROR_SUCCESS;

            } else {

                DebugMsg((DM_WARNING, TEXT("ApplySecurityToRegistryTree:  Registry enumeration failed with error = %d"), Error));
            }

            break;
        }


        //
        // Open the sub-key
        //

        Error = RegOpenKeyEx(RootKey,
                             SubKeyName,
                             0,
                             WRITE_DAC | KEY_ENUMERATE_SUB_KEYS | READ_CONTROL,
                             &SubKey);

        if (Error != ERROR_SUCCESS) {
            DebugMsg((DM_WARNING, TEXT("ApplySecurityToRegistryKey : Failed to open sub-key %s, error = %d"), SubKeyName, Error));
            break;
        }

        //
        // Apply security to the sub-tree
        //

        Error = ApplySecurityToRegistryTree(SubKey, pSD);


        //
        // We're finished with the sub-key
        //

        IgnoreError = RegCloseKey(SubKey);
        if (IgnoreError != ERROR_SUCCESS) {
            DebugMsg((DM_WARNING, TEXT("ApplySecurityToRegistryKey : Failed to close registry key, error = %d"), Error));
        }

        //
        // See if we set the security on the sub-tree successfully.
        //

        if (Error != ERROR_SUCCESS) {
            DebugMsg((DM_WARNING, TEXT("ApplySecurityToRegistryKey : Failed to apply security to sub-key %s, error = %d"), SubKeyName, Error));
            break;
        }

        //
        // Go enumerate the next sub-key
        //

        SubKeyIndex ++;
    }


    GlobalFree (SubKeyName);

    return Error;

}



//*************************************************************
//
//  SetupNewHive()
//
//  Purpose:    Initializes the new user hive created by copying
//              the default hive.
//
//  Parameters: lpProfile       -   Profile Information
//              lpSidString     -   Sid string
//              pSid            -   Sid (used by CreateNewUser)
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              7/18/95     ericflo    Created
//
//*************************************************************

BOOL SetupNewHive(LPPROFILE lpProfile, LPTSTR lpSidString, PSID pSid)
{
    DWORD Error, IgnoreError;
    HKEY RootKey;
    SECURITY_DESCRIPTOR sd;
    SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
    PACL pAcl = NULL;
    PSID  psidUser = NULL, psidSystem = NULL, psidAdmin = NULL;
    DWORD cbAcl, AceIndex;
    ACE_HEADER * lpAceHeader;
    BOOL bRetVal = FALSE;
    BOOL bFreeSid = TRUE;
    DWORD dwFlags = 0;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("SetupNewHive:  Entering")));


    //
    // Create the security descriptor that will be applied to each key
    //

    //
    // Give the user access by their real sid so they still have access
    // when they logoff and logon again
    //

    if (pSid) {
        psidUser = pSid;
        bFreeSid = FALSE;
        dwFlags = PI_NOUI;
    } else {
        psidUser = GetUserSid(lpProfile->hToken);
        dwFlags = lpProfile->dwFlags;
    }

    if (!psidUser) {
        DebugMsg((DM_WARNING, TEXT("SetupNewHive:  Failed to get user sid")));
        return FALSE;
    }



    //
    // Get the system sid
    //

    if (!AllocateAndInitializeSid(&authNT, 1, SECURITY_LOCAL_SYSTEM_RID,
                                  0, 0, 0, 0, 0, 0, 0, &psidSystem)) {
         DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to initialize system sid.  Error = %d"), GetLastError()));
         goto Exit;
    }


    //
    // Get the admin sid
    //

    if (!AllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
                                  DOMAIN_ALIAS_RID_ADMINS, 0, 0,
                                  0, 0, 0, 0, &psidAdmin)) {
         DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to initialize admin sid.  Error = %d"), GetLastError()));
         goto Exit;
    }


    //
    // Allocate space for the ACL
    //

    cbAcl = (2 * GetLengthSid (psidUser)) + (2 * GetLengthSid (psidSystem)) +
            (2 * GetLengthSid (psidAdmin)) + sizeof(ACL) +
            (6 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));


    pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl);
    if (!pAcl) {
        goto Exit;
    }


    if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to initialize acl.  Error = %d"), GetLastError()));
        goto Exit;
    }



    //
    // Add Aces for User, System, and Admin.  Non-inheritable ACEs first
    //

    AceIndex = 0;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, KEY_ALL_ACCESS, psidUser)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to add ace for user.  Error = %d"), GetLastError()));
        goto Exit;
    }


    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, KEY_ALL_ACCESS, psidSystem)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to add ace for system.  Error = %d"), GetLastError()));
        goto Exit;
    }

    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, KEY_ALL_ACCESS, psidAdmin)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to add ace for admin.  Error = %d"), GetLastError()));
        goto Exit;
    }


    //
    // Now the inheritable ACEs
    //

    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidUser)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to add ace for user.  Error = %d"), GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, AceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to get ace (%d).  Error = %d"), AceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidSystem)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to add ace for system.  Error = %d"), GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, AceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to get ace (%d).  Error = %d"), AceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    AceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidAdmin)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to add ace for admin.  Error = %d"), GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, AceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to get ace (%d).  Error = %d"), AceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    //
    // Put together the security descriptor
    //

    if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to initialize security descriptor.  Error = %d"), GetLastError()));
        goto Exit;
    }


    if (!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
        DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to set security descriptor dacl.  Error = %d"), GetLastError()));
        goto Exit;
    }


    //
    // Open the root of the user's profile
    //

    Error = RegOpenKeyEx(HKEY_USERS,
                         lpSidString,
                         0,
                         WRITE_DAC | KEY_ENUMERATE_SUB_KEYS | READ_CONTROL,
                         &RootKey);

    if (Error != ERROR_SUCCESS) {

        DebugMsg((DM_WARNING, TEXT("SetupNewHive: Failed to open root of user registry, error = %d"), Error));

    } else {

        //
        // Set the security descriptor on the entire tree
        //

        Error = ApplySecurityToRegistryTree(RootKey, &sd);


        if (Error == ERROR_SUCCESS) {

            TCHAR szSubKey[MAX_PATH];

            //
            // Change the security on certain keys in the user's registry
            // so that only Admin's and the OS have write access.
            //

            lstrcpy (szSubKey, lpSidString);
            lstrcat (szSubKey, TEXT("\\"));
            lstrcat (szSubKey, POLICIES_KEY);

            if (!SecureUserKey(lpProfile, szSubKey, pSid)) {
                DebugMsg((DM_WARNING, TEXT("SetupNewHive: Failed to secure policies key")));
            }

            bRetVal = TRUE;

        } else {

            DebugMsg((DM_WARNING, TEXT("SetupNewHive:  Failed to apply security to user registry tree, error = %d"), Error));
            ReportError(dwFlags, IDS_SECURITY_FAILED, Error);

        }

        RegFlushKey (RootKey);

        IgnoreError = RegCloseKey(RootKey);
        if (IgnoreError != ERROR_SUCCESS) {
            DebugMsg((DM_WARNING, TEXT("SetupNewHive:  Failed to close reg key, error = %d"), IgnoreError));
        }
    }


Exit:

    //
    // Free the sids and acl
    //

    if (bFreeSid && psidUser) {
        DeleteUserSid (psidUser);
    }

    if (psidSystem) {
        FreeSid(psidSystem);
    }

    if (psidAdmin) {
        FreeSid(psidAdmin);
    }

    if (pAcl) {
        GlobalFree (pAcl);
    }


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("SetupNewHive:  Leaving with a return value of %d"), bRetVal));


    return(bRetVal);

}


//*************************************************************
//
//  IsCentralProfileReachable()
//
//  Purpose:    Checks to see if the user can access the
//              central profile.
//
//  Parameters: lpProfile             - User's token
//              bCreateCentralProfile - Should the central profile be created
//              bMandatory            - Is this a mandatory profile
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/20/95     ericflo    Ported
//
//*************************************************************

BOOL IsCentralProfileReachable(LPPROFILE lpProfile, BOOL *bCreateCentralProfile,
                               BOOL *bMandatory)
{
    WIN32_FIND_DATA fd;
    HANDLE  hFile;
    TCHAR   szProfile[MAX_PATH];
    LPTSTR  lpProfilePath, lpEnd;
    BOOL    bRetVal = FALSE;
    DWORD   dwError;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Entering")));


    //
    // Setup default values
    //

    *bMandatory = FALSE;
    *bCreateCentralProfile = FALSE;


    //
    // Check parameters
    //

    if (lpProfile->szCentralProfile[0] == TEXT('\0')) {
        DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Null path.  Leaving")));
        return FALSE;
    }


    lpProfilePath = lpProfile->szCentralProfile;


    //
    // Make sure we don't overrun our temporary buffer
    //

    if ((lstrlen(lpProfilePath) + 1 + lstrlen(c_szNTUserMan + 1)) > MAX_PATH) {
        DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Failed because temporary buffer is too small.")));
        return FALSE;
    }


    //
    // Copy the profile path to a temporary buffer
    // we can munge it.
    //

    lstrcpy (szProfile, lpProfilePath);


    //
    // Add the slash if appropriate and then tack on
    // ntuser.man.
    //

    lpEnd = CheckSlash(szProfile);
    lstrcpy(lpEnd, c_szNTUserMan);


    //
    // Impersonate the user
    //


    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("IsCentralProfileReachable: Failed to impersonate user")));
        return FALSE;
    }


    //
    // See if this file exists
    //

    DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Testing <%s>"), szProfile));

    hFile = CreateFile(szProfile, GENERIC_READ, FILE_SHARE_READ, NULL,
                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hFile != INVALID_HANDLE_VALUE) {
        DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Found a mandatory profile.")));
        CloseHandle(hFile);
        *bMandatory = TRUE;
        bRetVal = TRUE;
        goto Exit;
    }


    dwError = GetLastError();
    DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Profile is not reachable, error = %d"),
                          dwError));


    //
    // If we received an error other than file not
    // found, bail now because we won't be able to
    // access this location.
    //

    if (dwError != ERROR_FILE_NOT_FOUND) {
        DebugMsg((DM_WARNING, TEXT("IsCentralProfileReachable:  Profile path <%s> is not reachable, error = %d"),
                                        szProfile, dwError));
        goto Exit;
    }


    //
    // Now try ntuser.dat
    //

    lstrcpy(lpEnd, c_szNTUserDat);


    //
    // See if this file exists.
    //

    DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Testing <%s>"), szProfile));

    hFile = CreateFile(szProfile, GENERIC_READ, FILE_SHARE_READ, NULL,
                       OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hFile != INVALID_HANDLE_VALUE) {
        DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Found a user profile.")));
        CloseHandle(hFile);
        bRetVal = TRUE;
        goto Exit;
    }


    dwError = GetLastError();
    DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Profile is not reachable, error = %d"),
                          dwError));


    if (dwError == ERROR_FILE_NOT_FOUND) {
        DebugMsg((DM_VERBOSE, TEXT("IsCentralProfileReachable:  Ok to create a user profile.")));
        *bCreateCentralProfile = TRUE;
        bRetVal = TRUE;
        goto Exit;
    }


    DebugMsg((DM_WARNING, TEXT("IsCentralProfileReachable:  Profile path <%s> is not reachable(2), error = %d"),
                                    szProfile, dwError));

Exit:

    //
    // Go back to system security context
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("IsCentralProfileReachable: Failed to revert to self")));
    }

    return bRetVal;
}

//*************************************************************
//
//  MyRegLoadKey()
//
//  Purpose:    Loads a hive into the registry
//
//  Parameters: lpProfile   -   Profile Info
//              hKey        -   Key to load the hive into
//              lpSubKey    -   Subkey name
//              lpFile      -   hive filename
//
//  Return:     ERROR_SUCCESS if successful
//              Error number if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/22/95     ericflo    Created
//
//*************************************************************

LONG MyRegLoadKey(LPPROFILE lpProfile, HKEY hKey,
                  LPTSTR lpSubKey, LPTSTR lpFile)
{
    NTSTATUS Status;
    BOOLEAN WasEnabled;
    int error;

    //
    // Enable the restore privilege
    //

    Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled);

    if (NT_SUCCESS(Status)) {

        error = RegLoadKey(hKey, lpSubKey, lpFile);

        //
        // Restore the privilege to its previous state
        //

        Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled);
        if (!NT_SUCCESS(Status)) {
            DebugMsg((DM_WARNING, TEXT("MyRegLoadKey:  Failed to restore RESTORE privilege to previous enabled state")));
        }


        //
        // Check if the hive was loaded
        //

        if (error != ERROR_SUCCESS) {
            ReportError(PI_NOUI, IDS_REGLOADKEYFAILED, error, lpFile);
            DebugMsg((DM_WARNING, TEXT("MyRegLoadKey:  Failed to load subkey <%s>, error =%d"), lpSubKey, error));
        }

    } else {
        error = GetLastError();
        DebugMsg((DM_WARNING, TEXT("MyRegLoadKey:  Failed to enable restore privilege to load registry key")));
    }

    return error;
}


//*************************************************************
//
//  MyRegUnLoadKey()
//
//  Purpose:    Unloads a registry key
//
//  Parameters: hKey          -  Registry handle
//              lpSubKey      -  Subkey to be unloaded
//
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/19/95     ericflo    Ported
//
//*************************************************************

BOOL MyRegUnLoadKey(HKEY hKey, LPTSTR lpSubKey)
{
    BOOL bResult = TRUE;
    LONG error;
    NTSTATUS Status;
    BOOLEAN WasEnabled;


    //
    // Enable the restore privilege
    //

    Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, TRUE, FALSE, &WasEnabled);

    if (NT_SUCCESS(Status)) {

        error = RegUnLoadKey(hKey, lpSubKey);

        if ( error != ERROR_SUCCESS) {
            bResult = FALSE;
        }

        //
        // Restore the privilege to its previous state
        //

        Status = RtlAdjustPrivilege(SE_RESTORE_PRIVILEGE, WasEnabled, FALSE, &WasEnabled);
        if (!NT_SUCCESS(Status)) {
            DebugMsg((DM_WARNING, TEXT("MyRegUnLoadKey:  Failed to restore RESTORE privilege to previous enabled state")));
        }

    } else {
        DebugMsg((DM_WARNING, TEXT("MyRegUnloadKey:  Failed to enable restore privilege to unload registry key")));
        bResult = FALSE;
    }

    return bResult;
}

//*************************************************************
//
//  UpgradeLocalProfile()
//
//  Purpose:    Upgrades a local profile from a 3.x profile
//              to a profile directory structure.
//
//  Parameters: lpProfile       -   Profile Information
//              lpOldProfile    -   Previous profile file
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              7/6/95      ericflo    Created
//
//*************************************************************

BOOL UpgradeLocalProfile (LPPROFILE lpProfile, LPTSTR lpOldProfile)
{
    TCHAR szSrc[MAX_PATH];
    TCHAR szDest[MAX_PATH];
    LPTSTR lpSrcEnd, lpDestEnd;
    BOOL bRetVal = FALSE;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("UpgradeLocalProfile:  Entering")));


    //
    // Setup the temporary buffers
    //

    lstrcpy (szSrc, lpOldProfile);
    lstrcpy (szDest, lpProfile->szLocalProfile);

    lpDestEnd = CheckSlash (szDest);
    lstrcpy (lpDestEnd, c_szNTUserDat);


    //
    // Copy the hive
    //

    if (!CopyFile(szSrc, szDest, FALSE)) {
        DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: CopyFile failed to copy hive with error = %d"),
                 GetLastError()));
        return FALSE;
    }


    //
    // Delete the old hive
    //

    DeleteFile (szSrc);



    //
    // Copy log file
    //

    lstrcat (szSrc, c_szLog);
    lstrcat (szDest, c_szLog);


    if (!CopyFile(szSrc, szDest, FALSE)) {
        DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: CopyFile failed to copy hive log with error = %d"),
                 GetLastError()));
    }


    //
    // Delete the old hive log
    //

    DeleteFile (szSrc);


    //
    // Copy in the new shell folders from the default
    //

    if ( !(lpProfile->dwInternalFlags & DEFAULT_NET_READY) ) {

        //
        // Impersonate the user
        //

        if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
            DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: Failed to impersonate user")));
            goto IssueLocalDefault;
        }


        CheckNetDefaultProfile (lpProfile);


        //
        // Go back to system security context
        //

        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: Failed to revert to self")));
        }

    }

    if (lpProfile->szDefaultProfile && *lpProfile->szDefaultProfile) {

        ExpandEnvironmentStrings(lpProfile->szDefaultProfile, szSrc, MAX_PATH);

        if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
            DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: Failed to impersonate user")));
            goto IssueLocalDefault;
        }

        if (CopyProfileDirectory (szSrc, lpProfile->szLocalProfile,
                                  CPD_IGNOREHIVE | CPD_IGNORECOPYERRORS)) {

            bRetVal = TRUE;
        }

        //
        // Go back to system security context
        //

        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: Failed to revert to self")));
        }
    }


IssueLocalDefault:

    if (!bRetVal) {

        ExpandEnvironmentStrings(DEFAULT_PROFILE, szSrc, MAX_PATH);

        if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
            DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: Failed to impersonate user")));
            goto Exit;
        }

        bRetVal = CopyProfileDirectory (szSrc, lpProfile->szLocalProfile,
                                        CPD_IGNOREHIVE | CPD_IGNORECOPYERRORS);

        //
        // Go back to system security context
        //

        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("UpgradeLocalProfile: Failed to revert to self")));
        }
    }

Exit:

    if (bRetVal) {
        lpProfile->dwInternalFlags |= PROFILE_RUN_SYNCAPP;
    }

    return bRetVal;
}

//*************************************************************
//
//  UpgradeCentralProfile()
//
//  Purpose:    Upgrades a central profile from a 3.x profile
//              to a profile directory structure.
//
//  Parameters: lpProfile       -   Profile Information
//              lpOldProfile    -   Previous profile file
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              7/6/95      ericflo    Created
//
//*************************************************************

BOOL UpgradeCentralProfile (LPPROFILE lpProfile, LPTSTR lpOldProfile)
{
    TCHAR szSrc[MAX_PATH];
    TCHAR szDest[MAX_PATH];
    LPTSTR lpSrcEnd, lpDestEnd, lpDot;
    BOOL bRetVal = FALSE;
    BOOL bMandatory = FALSE;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("UpgradeCentralProfile:  Entering")));


    //
    // Impersonate the user
    //

    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("UpgradeCentralProfile: Failed to impersonate user")));
        return FALSE;
    }


    //
    // Setup the source buffer
    //

    lstrcpy (szSrc, lpOldProfile);


    //
    // Determine the profile type
    //

    lpDot = szSrc + lstrlen(szSrc) - 4;

    if (*lpDot == TEXT('.')) {
        if (!lstrcmpi (lpDot, c_szMAN)) {
            bMandatory = TRUE;
        }
    }


    //
    // Setup the destination buffer
    //

    lstrcpy (szDest, lpProfile->szCentralProfile);

    lpDestEnd = CheckSlash (szDest);

    if (bMandatory) {
        lstrcpy (lpDestEnd, c_szNTUserMan);
    } else {
        lstrcpy (lpDestEnd, c_szNTUserDat);
    }


    //
    // Copy the hive
    //

    if (!CopyFile(szSrc, szDest, FALSE)) {
        DebugMsg((DM_WARNING, TEXT("UpgradeCentralProfile: CopyFile failed to copy hive with error = %d"),
                 GetLastError()));
        DebugMsg((DM_WARNING, TEXT("UpgradeCentralProfile: Source = <%s>"), szSrc));
        DebugMsg((DM_WARNING, TEXT("UpgradeCentralProfile: Destination = <%s>"), szDest));

        goto Exit;
    }



    //
    // Copy log file
    //

    lstrcpy (lpDot, c_szLog);
    lstrcat (szDest, c_szLog);


    if (!CopyFile(szSrc, szDest, FALSE)) {
        DebugMsg((DM_VERBOSE, TEXT("UpgradeCentralProfile: CopyFile failed to copy hive log with error = %d"),
                 GetLastError()));
        DebugMsg((DM_VERBOSE, TEXT("UpgradeCentralProfile: Source = <%s>"), szSrc));
        DebugMsg((DM_VERBOSE, TEXT("UpgradeCentralProfile: Destination = <%s>"), szDest));

    }


    //
    // Copy in the new shell folders from the default
    //

    if ( !(lpProfile->dwInternalFlags & DEFAULT_NET_READY) ) {
        CheckNetDefaultProfile (lpProfile);
    }


    if (lpProfile->szDefaultProfile && *lpProfile->szDefaultProfile) {

        ExpandEnvironmentStrings(lpProfile->szDefaultProfile, szSrc, MAX_PATH);

        if (CopyProfileDirectory (szSrc, lpProfile->szCentralProfile,
                                  CPD_IGNOREHIVE | CPD_IGNORECOPYERRORS)) {

            bRetVal = TRUE;
        }
    }


    if (!bRetVal) {

        ExpandEnvironmentStrings(DEFAULT_PROFILE, szSrc, MAX_PATH);

        bRetVal = CopyProfileDirectory (szSrc, lpProfile->szCentralProfile,
                                        CPD_IGNOREHIVE | CPD_IGNORECOPYERRORS);
    }


    if (bRetVal) {
        lpProfile->dwInternalFlags |= PROFILE_RUN_SYNCAPP;
    }


Exit:

    //
    // Go back to system security context
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("UpgradeCentralProfile: Failed to revert to self")));
    }


    return bRetVal;
}

//*************************************************************
//
//  GetTempProfileDir()
//
//  Purpose:    Generates a temporary profile directory
//
//  Parameters: lpProfile       -   Profile Information
//              lpProfileImage  -   Receives the generated directory
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/20/95     ericflo    Created
//
//*************************************************************

BOOL GetTempProfileDir (LPPROFILE lpProfile, LPTSTR lpProfileImage)
{
    TCHAR szProfileImage[MAX_PATH];
    TCHAR szExpProfileImage[MAX_PATH];


    //
    // Initialize the profile image
    //

    lstrcpy (szProfileImage, CONFIG_FILE_PATH);


    //
    // Call the compute function to do the real work.
    //

    if (ComputeLocalProfileName (lpProfile, TEMP_PROFILE_NAME_BASE,
                             szProfileImage, MAX_PATH,
                             szExpProfileImage, MAX_PATH)) {

        //
        // Save the generated name.
        //

        lstrcpy (lpProfileImage, szExpProfileImage);

        return TRUE;
    }

    return FALSE;
}

//*************************************************************
//
//  CreateSecureDirectory()
//
//  Purpose:    Creates a secure directory that only the user,
//              admin, and system have access to.
//
//  Parameters: lpProfile   -   Profile Information
//              lpDirectory -   Directory Name
//              pSid        -   Sid (used by CreateUserProfile)
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              7/20/95     ericflo    Created
//
//*************************************************************

BOOL CreateSecureDirectory (LPPROFILE lpProfile, LPTSTR lpDirectory, PSID pSid)
{
    SECURITY_DESCRIPTOR sd;
    SECURITY_ATTRIBUTES sa;
    SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
    PACL pAcl = NULL;
    PSID  psidUser = NULL, psidSystem = NULL, psidAdmin = NULL;
    DWORD cbAcl, aceIndex;
    ACE_HEADER * lpAceHeader;
    BOOL bRetVal = FALSE;
    BOOL bFreeSid = TRUE;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Entering with <%s>"), lpDirectory));



    //
    // Get the SIDs we'll need for the DACL
    //

    if (pSid) {
        psidUser = pSid;
        bFreeSid = FALSE;
    } else {
        psidUser = GetUserSid(lpProfile->hToken);
    }



    //
    // Get the system sid
    //

    if (!AllocateAndInitializeSid(&authNT, 1, SECURITY_LOCAL_SYSTEM_RID,
                                  0, 0, 0, 0, 0, 0, 0, &psidSystem)) {
         DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to initialize system sid.  Error = %d"), GetLastError()));
         goto Exit;
    }


    //
    // Get the Admin sid
    //

    if (!AllocateAndInitializeSid(&authNT, 2, SECURITY_BUILTIN_DOMAIN_RID,
                                  DOMAIN_ALIAS_RID_ADMINS, 0, 0,
                                  0, 0, 0, 0, &psidAdmin)) {
         DebugMsg((DM_VERBOSE, TEXT("SetupNewHive: Failed to initialize admin sid.  Error = %d"), GetLastError()));
         goto Exit;
    }


    //
    // Allocate space for the ACL
    //

    cbAcl = (2 * GetLengthSid (psidUser)) + (2 * GetLengthSid (psidSystem)) +
            (2 * GetLengthSid (psidAdmin)) + sizeof(ACL) +
            (6 * (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)));


    pAcl = (PACL) GlobalAlloc(GMEM_FIXED, cbAcl);
    if (!pAcl) {
        goto Exit;
    }


    if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to initialize acl.  Error = %d"), GetLastError()));
        goto Exit;
    }



    //
    // Add Aces for User, System, and Admin.  Non-inheritable ACEs first
    //

    aceIndex = 0;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidUser)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to add ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }



    aceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidSystem)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to add ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }


    aceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, FILE_ALL_ACCESS, psidAdmin)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to add ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }


    //
    // Now the inheritable ACEs
    //

    aceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidUser)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to add ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, aceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to get ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);



    aceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidSystem)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to add ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, aceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to get ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);


    aceIndex++;
    if (!AddAccessAllowedAce(pAcl, ACL_REVISION, GENERIC_ALL, psidAdmin)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to add ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }

    if (!GetAce(pAcl, aceIndex, &lpAceHeader)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to get ace (%d).  Error = %d"), aceIndex, GetLastError()));
        goto Exit;
    }

    lpAceHeader->AceFlags |= (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);



    //
    // Put together the security descriptor
    //

    if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to initialize security descriptor.  Error = %d"), GetLastError()));
        goto Exit;
    }


    if (!SetSecurityDescriptorDacl(&sd, TRUE, pAcl, FALSE)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to set security descriptor dacl.  Error = %d"), GetLastError()));
        goto Exit;
    }


    //
    // Add the security descriptor to the sa structure
    //

    sa.nLength = sizeof(sa);
    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;


    //
    // Attempt to create the directory
    //

    if (CreateNestedDirectory(lpDirectory, &sa)) {
        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Created the directory <%s>"), lpDirectory));
        bRetVal = TRUE;

    } else {

        DebugMsg((DM_VERBOSE, TEXT("CreateSecureDirectory: Failed to created the directory <%s>"), lpDirectory));
    }



Exit:

    if (bFreeSid && psidUser) {
        DeleteUserSid (psidUser);
    }

    if (psidSystem) {
        FreeSid(psidSystem);
    }

    if (psidAdmin) {
        FreeSid(psidAdmin);
    }

    if (pAcl) {
        GlobalFree (pAcl);
    }

    return bRetVal;

}


//*************************************************************
//
//  ComputeLocalProfileName()
//
//  Purpose:    Constructs the pathname of the local profile
//              for this user.  It will attempt to create
//              a directory of the username, and then if
//              unsccessful it will try the username.xxx
//              where xxx is a three digit number
//
//  Parameters: lpProfile             -   Profile Information
//              lpUserName            -   UserName
//              lpProfileImage        -   Profile directory (unexpanded)
//              cchMaxProfileImage    -   lpProfileImage buffer size
//              lpExpProfileImage     -   Expanded directory
//              cchMaxExpProfileImage -   lpExpProfileImage buffer size
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:   lpProfileImage should be initialized with the
//              root profile path and the trailing backslash.
//
//  History:    Date        Author     Comment
//              6/20/95     ericflo    Created
//
//*************************************************************

BOOL ComputeLocalProfileName (LPPROFILE lpProfile, LPTSTR lpUserName,
                              LPTSTR lpProfileImage, DWORD  cchMaxProfileImage,
                              LPTSTR lpExpProfileImage, DWORD  cchMaxExpProfileImage)
{
    int i = 0;
    TCHAR szNumber[5];
    LPTSTR lpEnd;
    DWORD dwProfileLen;
    BOOL bRetVal = FALSE;
    HANDLE hFile;
    WIN32_FIND_DATA fd;


    //
    // Check buffer size
    //

    dwProfileLen = lstrlen(lpProfileImage);
    if ((dwProfileLen + lstrlen(lpUserName) + 4 + 1) > cchMaxProfileImage) {
        DebugMsg((DM_VERBOSE, TEXT("ComputeLocalProfileName: buffer too small")));
        return FALSE;
    }

    //
    // Place the username onto the end of the profile image
    //

    lpEnd = lpProfileImage + dwProfileLen;
    lstrcpy (lpEnd, lpUserName);


    //
    // Expand the profile path
    //

    ExpandEnvironmentStrings(lpProfileImage, lpExpProfileImage, cchMaxExpProfileImage);



    //
    // Does this directory exist?
    //

    hFile = FindFirstFile (lpExpProfileImage, &fd);

    if (hFile == INVALID_HANDLE_VALUE) {

        //
        // Attempt to create the directory
        //

        if (CreateSecureDirectory(lpProfile, lpExpProfileImage, NULL)) {
            DebugMsg((DM_VERBOSE, TEXT("ComputeLocalProfileName: generated the profile directory <%s>"), lpExpProfileImage));
            bRetVal = TRUE;
            goto Exit;
        }

    } else {

        FindClose (hFile);
    }


    //
    // Failed to create the directory for some reason.
    // Now try username.000, username.001, etc
    //

    lpEnd = lpProfileImage + lstrlen(lpProfileImage);

    for (i=0; i < 1000; i++) {

        //
        // Convert the number to a string and attach it.
        //

        wsprintf (szNumber, TEXT(".%.3d"), i);
        lstrcpy (lpEnd, szNumber);


        //
        // Expand the profile path
        //

        ExpandEnvironmentStrings(lpProfileImage, lpExpProfileImage, cchMaxExpProfileImage);


        //
        // Does this directory exist?
        //

        hFile = FindFirstFile (lpExpProfileImage, &fd);

        if (hFile == INVALID_HANDLE_VALUE) {

            //
            // Attempt to create the directory
            //

            if (CreateSecureDirectory(lpProfile, lpExpProfileImage, NULL)) {
                DebugMsg((DM_VERBOSE, TEXT("ComputeLocalProfileName: generated the profile directory <%s>"), lpExpProfileImage));
                bRetVal = TRUE;
                goto Exit;
            }

        } else {

            FindClose (hFile);
        }
    }


    DebugMsg((DM_WARNING, TEXT("ComputeLocalProfileName: Could not generate a profile directory.  Error = %d"), GetLastError()));

Exit:


    return bRetVal;
}

//*************************************************************
//
//  CreateLocalProfileKey()
//
//  Purpose:    Creates a registry key pointing at the user profile
//
//  Parameters: lpProfile   -   Profile information
//              phKey       -   Handle to registry key if successful
//              bKeyExists  -   TRUE if the registry key already existed
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/20/95     ericflo    Ported
//
//*************************************************************

BOOL CreateLocalProfileKey (LPPROFILE lpProfile, PHKEY phKey, BOOL *bKeyExists)
{
    TCHAR LocalProfileKey[MAX_PATH];
    DWORD Disposition;
    DWORD RegErr = ERROR_SUCCESS + 1;
    BOOL Result;
    LPTSTR SidString;


    SidString = GetSidString(lpProfile->hToken);
    if (SidString != NULL) {

        //
        // Call the RegCreateKey api in the user's context
        //

        lstrcpy(LocalProfileKey, PROFILE_LIST_PATH);
        lstrcat(LocalProfileKey, TEXT("\\"));
        lstrcat(LocalProfileKey, SidString);

        RegErr = RegCreateKeyEx(HKEY_LOCAL_MACHINE, LocalProfileKey, 0, 0, 0,
                                KEY_READ | KEY_WRITE, NULL, phKey, &Disposition);
        if (RegErr == ERROR_SUCCESS) {
            *bKeyExists = (BOOL)(Disposition & REG_OPENED_EXISTING_KEY);
        } else {
           DebugMsg((DM_WARNING, TEXT("CreateLocalProfileKey:  Failed trying to create the local profile key <%s>, error = %d."), LocalProfileKey, RegErr));
        }

        DeleteSidString(SidString);
    }


    return(RegErr == ERROR_SUCCESS);
}


//*************************************************************
//
//  GetLocalProfileImage()
//
//  Purpose:    Create/opens the profileimagepath
//
//  Parameters: lpProfile   -   Profile information
//              bNewUser    -   set to TRUE if the default profile was issued.
//
//  Return:     TRUE if the profile image is reachable
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/20/95     ericflo    Ported
//
//*************************************************************
BOOL GetLocalProfileImage(LPPROFILE lpProfile, BOOL *bNewUser)
{
    HKEY hKey;
    BOOL bKeyExists;
    TCHAR lpProfileImage[MAX_PATH];
    TCHAR lpExpProfileImage[MAX_PATH];
    TCHAR lpOldProfileImage[MAX_PATH];
    LPTSTR lpExpandedPath, lpEnd;
    DWORD cbExpProfileImage = sizeof(TCHAR)*MAX_PATH;
    HANDLE hFile;
    WIN32_FIND_DATA fd;
    DWORD cb;
    DWORD err;
    DWORD dwType;
    HANDLE fh;
    PSID UserSid;
    BOOL bRetVal = FALSE;
    BOOL bUpgradeLocal = FALSE;

    lpProfile->szLocalProfile[0] = TEXT('\0');
    *bNewUser = TRUE;


    if (!CreateLocalProfileKey(lpProfile, &hKey, &bKeyExists)) {
        return FALSE;   // not reachable and cannot keep a local copy
    }

    if (bKeyExists) {

        //
        // Check if the local profile image is valid.
        //

        DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Found entry in profile list for existing local profile")));

        err = RegQueryValueEx(hKey, PROFILE_IMAGE_VALUE_NAME, 0, &dwType,
                                  (LPBYTE)lpExpProfileImage, &cbExpProfileImage);
        if (err == ERROR_SUCCESS && cbExpProfileImage) {
            DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Local profile image filename = <%s>"), lpExpProfileImage));

            if (dwType == REG_EXPAND_SZ) {

                //
                // Expand the profile image filename
                //

                cb = sizeof(lpExpProfileImage);
                lpExpandedPath = LocalAlloc(LPTR, cb);
                if (lpExpandedPath) {
                    ExpandEnvironmentStrings(lpExpProfileImage, lpExpandedPath, cb);
                    lstrcpy(lpExpProfileImage, lpExpandedPath);
                    LocalFree(lpExpandedPath);
                }

                DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Expanded local profile image filename = <%s>"), lpExpProfileImage));
            }


            //
            //  Call FindFirst to see if we need to migrate this profile
            //

            hFile = FindFirstFile (lpExpProfileImage, &fd);

            if (hFile == INVALID_HANDLE_VALUE) {
                DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Local profile image filename we got from our profile list doesn't exit.  Error = %d"), GetLastError()));
                goto CreateLocal;
            }

            FindClose(hFile);


            //
            // If this is a file, then we need to migrate it to
            //

            if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
                lstrcpy (lpOldProfileImage, lpExpProfileImage);
                bUpgradeLocal = TRUE;
                goto CreateLocal;
            }


            //
            // Test if a mandatory profile exists
            //

            lpEnd = CheckSlash (lpExpProfileImage);
            lstrcpy (lpEnd, c_szNTUserMan);

            fh = CreateFile(lpExpProfileImage, GENERIC_READ, FILE_SHARE_READ, NULL,
                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            if (fh != INVALID_HANDLE_VALUE) {
                lpProfile->dwInternalFlags |= PROFILE_MANDATORY;
                CloseHandle(fh);

                DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Found local mandatory profile image file ok <%s>"),
                         lpExpProfileImage));

                *(lpEnd - 1) = TEXT('\0');
                lstrcpy(lpProfile->szLocalProfile, lpExpProfileImage);
                RegCloseKey(hKey);
                *bNewUser = FALSE;
                return TRUE;  // local copy is valid and reachable
            } else {
                DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  No local mandatory profile.  Error = %d"), GetLastError()));
            }


            //
            // Test if a normal profile exists
            //

            lstrcpy (lpEnd, c_szNTUserDat);

            fh = CreateFile(lpExpProfileImage, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
            if (fh != INVALID_HANDLE_VALUE) {
                CloseHandle(fh);

                DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Found local profile image file ok <%s>"),
                         lpExpProfileImage));

                *(lpEnd - 1) = TEXT('\0');
                lstrcpy(lpProfile->szLocalProfile, lpExpProfileImage);
                RegCloseKey(hKey);
                *bNewUser = FALSE;
                return TRUE;  // local copy is valid and reachable
            } else {
                DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  Local profile image filename we got from our profile list doesn't exit.  <%s>  Error = %d"),
                         lpExpProfileImage, GetLastError()));
            }
        }
    }


CreateLocal:

    //
    // No local copy found, try to create a new one.
    //

    DebugMsg((DM_VERBOSE, TEXT("GetLocalProfileImage:  One way or another we haven't got an existing local profile, try and create one")));

    lstrcpy(lpProfileImage, CONFIG_FILE_PATH);
    if (ComputeLocalProfileName(lpProfile, lpProfile->szUserName,
                                lpProfileImage, MAX_PATH,
                                lpExpProfileImage, MAX_PATH)) {


        //
        // Add this image file to our profile list for this user
        //

        err = RegSetValueEx(hKey,
                            PROFILE_IMAGE_VALUE_NAME,
                            0,
                            REG_EXPAND_SZ,
                            (LPBYTE)lpProfileImage,
                            sizeof(TCHAR)*(lstrlen(lpProfileImage) + 1));

        if (err == ERROR_SUCCESS) {

            lstrcpy(lpProfile->szLocalProfile, lpExpProfileImage);

            //
            // Get the sid of the logged on user
            //

            UserSid = GetUserSid(lpProfile->hToken);
            if (UserSid != NULL) {

                //
                // Store the user sid under the Sid key of the local profile
                //

                err = RegSetValueEx(hKey,
                                    TEXT("Sid"),
                                    0,
                                    REG_BINARY,
                                    UserSid,
                                    RtlLengthSid(UserSid));


                if (err != ERROR_SUCCESS) {
                    DebugMsg((DM_WARNING, TEXT("GetLocalProfileImage:  Failed to set 'sid' value of user in profile list, error = %d"), err));
                }

                //
                // We're finished with the user sid
                //

                DeleteUserSid(UserSid);


                //
                // If we are upgrading a profile from a 3.5 machine
                // do that now.
                //

                if (bUpgradeLocal) {
                    if (UpgradeLocalProfile (lpProfile, lpOldProfileImage)) {
                        *bNewUser = FALSE;
                    }
                }

                bRetVal = TRUE;

            } else {
                DebugMsg((DM_WARNING, TEXT("GetLocalProfileImage:  Failed to get sid of logged on user, so unable to update profile list")));
            }
        } else {
            DebugMsg((DM_WARNING, TEXT("GetLocalProfileImage:  Failed to update profile list for user with local profile image filename, error = %d"), err));
        }
    }


    err = RegCloseKey(hKey);

    if (err != STATUS_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("GetLocalProfileImage:  Failed to close registry key, error = %d"), err));
    }

    return bRetVal;
}

//*************************************************************
//
//  UpdateToLatestProfile()
//
//  Purpose:    Determines which profile is newer, and
//              updates the local cache.
//
//  Parameters: lpProfile           -   Profile info
//              lpCentralProfile    -   Central profile
//              lpLocalProfile      -   Local profile
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/21/95     ericflo    Created
//
//*************************************************************

BOOL UpdateToLatestProfile(LPPROFILE lpProfile, LPTSTR lpCentralProfile,
                           LPTSTR lpLocalProfile, LPTSTR lpSidString)
{
    HANDLE hLocal;
    HANDLE hCentral;
    FILETIME ftLocal;
    FILETIME ftCentral;
    BOOL FromCentralToLocal;
    int DlgReturn;
    LONG lTimeCompare;
    TCHAR szProfile[MAX_PATH];
    LPTSTR lpEnd;
    BOOL bRetVal;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile: Entering.  Central = <%s>  Local = <%s>"),
             lpCentralProfile, lpLocalProfile));


    //
    // Setup a temporary buffer to work with
    //

    lstrcpy (szProfile, lpCentralProfile);
    lpEnd = CheckSlash (szProfile);

    if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
        lstrcpy (lpEnd, c_szNTUserMan);
    } else {
        lstrcpy (lpEnd, c_szNTUserDat);
    }


    //
    // Impersonate the user
    //


    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to impersonate user")));
        return FALSE;
    }


    //
    // Attempt to open the central profile
    //

    hCentral = CreateFile(szProfile, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hCentral == INVALID_HANDLE_VALUE) {
        DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: couldn't open central profile, error = %d"), GetLastError()));

        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to revert to self")));
        }

        return TRUE;

    } else {

        if (!GetFileTime(hCentral, NULL, NULL, &ftCentral)) {
            DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to query central profile file time.  Error = %d"),
                     GetLastError()));
            ftCentral.dwLowDateTime = 0;
            ftCentral.dwHighDateTime = 0;
        }
        CloseHandle(hCentral);
    }

    //
    // Revert to being 'ourself'
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to revert to self")));
    }




    //
    // Re-initialize the temporary buffer to look
    // at the local profile.
    //

    lstrcpy (szProfile, lpLocalProfile);
    lpEnd = CheckSlash (szProfile);

    if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
        lstrcpy (lpEnd, c_szNTUserMan);
    } else {
        lstrcpy (lpEnd, c_szNTUserDat);
    }


    //
    // Attempt to open the local profile
    //

    hLocal = CreateFile(szProfile, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);


    if (hLocal == INVALID_HANDLE_VALUE) {

        DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile: couldn't open local profile, error = %d"), GetLastError()));
        ftLocal.dwLowDateTime = 0;
        ftLocal.dwHighDateTime = 0;

    } else {

        if (!GetFileTime(hLocal, NULL, NULL, &ftLocal)) {
            DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to query local profile file time.  Error = %d"),
                     GetLastError()));
            ftLocal.dwLowDateTime = 0;
            ftLocal.dwHighDateTime = 0;
        }

        CloseHandle(hLocal);
    }


    if (lpProfile->dwInternalFlags & PROFILE_NEW_LOCAL) {
        DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile:  New local cach has been created.  Forcing copy from central.")));
        FromCentralToLocal = TRUE;

    } else {
        //
        // Decide which file is the most uptodate and use that as the source
        // for the copy
        //

        lTimeCompare = CompareFileTime(&ftCentral, &ftLocal);
        if (lTimeCompare == -1) {
            FromCentralToLocal = FALSE;
            DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile:  Local profile time stamp is newer than central time stamp.")));
        }
        else if (lTimeCompare == 1) {
            FromCentralToLocal = TRUE;
            DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile:  Central profile time stamp is newer than local time stamp.")));
        }
        else {
            DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile:  Central and local profile times match.")));
            return TRUE;
        }
    }


    //
    // If we have a mandatory profile and the cache is newer
    // than the central, force the central to be downloaded again.
    // We only want to use the cache if the central is not available.
    //

    if ((lpProfile->dwInternalFlags & PROFILE_MANDATORY) && !FromCentralToLocal) {

        FromCentralToLocal = TRUE;
    }



    if (!FromCentralToLocal && !(lpProfile->dwFlags & PI_NOUI)) {
        HKEY hKey;
        LONG lResult;
        DWORD dwType, dwSize, dwDlgTimeOut;


        //
        // Ask the user if ok to overwrite the central profile with the
        // the local profile.
        //
        // Get the dialog box timeout
        //

        dwDlgTimeOut = PROFILE_DLG_TIMEOUT;

        lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                               WINLOGON_KEY,
                               0,
                               KEY_READ,
                               &hKey);

        if (lResult == ERROR_SUCCESS) {

            dwSize = sizeof(DWORD);
            RegQueryValueEx (hKey,
                             TEXT("ProfileDlgTimeOut"),
                             NULL,
                             &dwType,
                             (LPBYTE) &dwDlgTimeOut,
                             &dwSize);


            RegCloseKey (hKey);
        }



        if (dwDlgTimeOut > 0) {

            DlgReturn = DialogBoxParam (g_hDllInstance, MAKEINTRESOURCE(IDD_CHOOSE_PROFILE),
                                        NULL, ChooseProfileDlgProc, dwDlgTimeOut);

            if (DlgReturn == IDNO) {
                //
                // The user doesn't want to overwrite the central profile.
                // The central profile becomes the active profile and overwrites
                // the local copy.
                //
                FromCentralToLocal = TRUE;
            }
        }
    }



    //
    // If FromCentralToLocal is false, we can exit now,
    // since we are going to use the cache.
    //

    if (!FromCentralToLocal) {
        return TRUE;
    }


    //
    // Impersonate the user
    //


    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to impersonate user")));
        return FALSE;
    }



    if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {

        bRetVal = CopyProfileDirectory (lpCentralProfile, lpLocalProfile,
                                        CPD_IGNORECOPYERRORS |
                                        CPD_COPYIFDIFFERENT |
                                        CPD_SYNCHRONIZE);

    } else {

        bRetVal = CopyProfileDirectory (lpCentralProfile, lpLocalProfile,
                                        CPD_IGNORECOPYERRORS |
                                        CPD_COPYIFDIFFERENT  |
                                        CPD_SYNCHRONIZE);
    }


    //
    // Revert to being 'ourself'
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile: Failed to revert to self")));
    }


    if (!bRetVal) {

        DebugMsg((DM_WARNING, TEXT("UpdateToLatestProfile:  CopyProfileDirectory returned FALSE.  Error = %d"), GetLastError()));
        return FALSE;

    }





    DebugMsg((DM_VERBOSE, TEXT("UpdateToLatestProfile:  Leaving successfully.")));

    return TRUE;
}


//*************************************************************
//
//  IssueDefaultProfile()
//
//  Purpose:    Issues the specified default profile to a user
//
//  Parameters: lpProfile         -   Profile Information
//              lpDefaultProfile  -   Default profile location
//              lpLocalProfile    -   Local profile location
//              lpSidString       -   User's sid
//              bMandatory        -   Issue mandatory profile
//
//  Return:     TRUE if profile was successfully setup
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/22/95     ericflo    Created
//
//*************************************************************

BOOL IssueDefaultProfile (LPPROFILE lpProfile, LPTSTR lpDefaultProfile,
                          LPTSTR lpLocalProfile, LPTSTR lpSidString,
                          BOOL bMandatory)
{
    LPTSTR lpEnd, lpTemp;
    TCHAR szProfile[MAX_PATH];
    TCHAR szTempProfile[MAX_PATH];
    BOOL bProfileLoaded = FALSE;
    WIN32_FIND_DATA fd;
    HANDLE hFile;
    LONG error;


    //
    // Verbose Output
    //

    DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  Entering.  lpDefaultProfile = <%s> lpLocalProfile = <%s>"),
             lpDefaultProfile, lpLocalProfile));


    //
    // First expand the default profile
    //

    ExpandEnvironmentStrings(lpDefaultProfile, szProfile, MAX_PATH);


    //
    // Does the default profile directory exist?
    //

    hFile = FindFirstFile (szProfile, &fd);

    if (hFile == INVALID_HANDLE_VALUE) {
        DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  Default profile <%s> does not exist."), szProfile));
        return FALSE;
    }

    FindClose(hFile);


    //
    // Impersonate the user
    //

    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("IssueDefaultProfile: Failed to impersonate user")));
        return FALSE;
    }

    //
    // Copy profile to user profile
    //

    if (!CopyProfileDirectory (szProfile, lpLocalProfile, CPD_FORCECOPY)) {

        DebugMsg((DM_WARNING, TEXT("IssueDefaultProfile:  CopyProfileDirectory returned FALSE.  Error = %d"), GetLastError()));
        return FALSE;
    }

    //
    // Rename the profile is a mandatory one was requested.
    //

    lstrcpy (szProfile, lpLocalProfile);
    lpEnd = CheckSlash (szProfile);

    if (bMandatory) {

        DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  Mandatory profile was requested.")));

        lstrcpy (szTempProfile, szProfile);
        lstrcpy (lpEnd, c_szNTUserMan);

        hFile = FindFirstFile (szProfile, &fd);

        if (hFile != INVALID_HANDLE_VALUE) {
            DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  Mandatory profile already exists.")));
            FindClose(hFile);

        } else {
            DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  Renaming ntuser.dat to ntuser.man")));

            lpTemp = CheckSlash(szTempProfile);
            lstrcpy (lpTemp, c_szNTUserDat);

            if (!MoveFile(szTempProfile, szProfile)) {
                DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  MoveFile returned false.  Error = %d"), GetLastError()));
            }
        }

    } else {
        lstrcpy (lpEnd, c_szNTUserDat);
    }

    //
    // Revert to being 'ourself'
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("IssueDefaultProfile: Failed to revert to self")));
    }


    //
    // Try to load the new profile
    //

    error = MyRegLoadKey(lpProfile,HKEY_USERS,
                                   lpSidString,
                                   szProfile);

    bProfileLoaded = (error == ERROR_SUCCESS);


    if (!bProfileLoaded) {
        DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  MyRegLoadKey failed with error %d"),
                 error));

        return FALSE;
    }


    //
    // Set the sync app flag
    //

    lpProfile->dwInternalFlags |= PROFILE_RUN_SYNCAPP;


    DebugMsg((DM_VERBOSE, TEXT("IssueDefaultProfile:  Leaving successfully")));

    return TRUE;
}


//*************************************************************
//
//  DeleteProfile()
//
//  Purpose:    Deletes the specified profile from the
//              registry and disk.
//
//  Parameters: lpSidString     -   Registry subkey
//              lpProfileDir    -   Profile directory
//              bBackup         -   Backup profile before deleting
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/23/95     ericflo    Created
//
//*************************************************************

BOOL DeleteProfile (LPTSTR lpSidString, LPTSTR lpLocalProfile, BOOL bBackup)
{
    LONG lResult;
    TCHAR szTemp[MAX_PATH];


    //
    // Cleanup the registry first.
    //

    if (lpSidString && *lpSidString) {

        lstrcpy(szTemp, PROFILE_LIST_PATH);
        lstrcat(szTemp, TEXT("\\"));
        lstrcat(szTemp, lpSidString);
        lResult = RegDeleteKey(HKEY_LOCAL_MACHINE, szTemp);

        if (lResult != ERROR_SUCCESS) {
            DebugMsg((DM_WARNING, TEXT("DeleteProfile:  Unable to delete registry entry.  Error = %d"), lResult));
            return FALSE;
        }
    }

    if (bBackup) {

        //
        // Generate the backup name
        //

        lstrcpy (szTemp, lpLocalProfile);
        lstrcat (szTemp, c_szBAK);

        //
        // First delete any previous backup
        //

        Delnode (szTemp);

        //
        // Attempt to rename the directory
        //

        if (!MoveFileEx(lpLocalProfile, szTemp, 0)) {

            DebugMsg((DM_VERBOSE, TEXT("DeleteProfile:  Failed to rename the directory.  Error = %d"), GetLastError()));
            return FALSE;
        }


    } else {

        if (!Delnode (lpLocalProfile)) {
            DebugMsg((DM_WARNING, TEXT("DeleteProfile:  Delnode failed.  Error = %d"), GetLastError()));
            return FALSE;
        }
    }

    return TRUE;
}

//*************************************************************
//
//  UpgradeProfile()
//
//  Purpose:    Called after a profile is successfully loaded.
//              Stamps build number into the profile, and if
//              appropriate upgrades the per-user settings
//              that NT setup wants done.
//
//  Parameters: lpProfile   -   Profile Information
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              7/7/95     ericflo    Created
//
//*************************************************************

BOOL UpgradeProfile (LPPROFILE lpProfile)
{
    HKEY hKey;
    DWORD dwDisp, dwType, dwSize, dwBuildNumber;
    LONG lResult;
    BOOL bUpgrade = FALSE;
    BOOL bRunSyncApp = FALSE;
    BOOL bDoUserdiff = TRUE;


    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("UpgradeProfile: Entering")));


    //
    // Query for the build number
    //

    lResult = RegCreateKeyEx (lpProfile->hKeyCurrentUser, WINLOGON_KEY,
                              0, NULL, REG_OPTION_NON_VOLATILE,
                              KEY_ALL_ACCESS, NULL, &hKey, &dwDisp);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("UpgradeProfile: Failed to open winlogon key. Error = %d"), lResult));
        return FALSE;
    }


    dwSize = sizeof(dwBuildNumber);
    lResult = RegQueryValueEx (hKey, PROFILE_BUILD_NUMBER,
                               NULL, &dwType, (LPBYTE)&dwBuildNumber,
                               &dwSize);

    if (lResult == ERROR_SUCCESS) {

        //
        // Found the build number.  If they match,
        // we don't want to process the userdiff hive
        //

        if (dwBuildNumber == g_dwBuildNumber) {
            DebugMsg((DM_VERBOSE, TEXT("UpgradeProfile: Build numbers match")));
            bDoUserdiff = FALSE;
        }
    } else {

        dwBuildNumber = 0;
    }


    if (bDoUserdiff) {

        //
        // Set the build number
        //

        lResult = RegSetValueEx (hKey, PROFILE_BUILD_NUMBER, 0, REG_DWORD,
                                 (LPBYTE) &g_dwBuildNumber, sizeof(g_dwBuildNumber));

        if (lResult != ERROR_SUCCESS) {
           DebugMsg((DM_WARNING, TEXT("UpgradeProfile: Failed to set build number. Error = %d"), lResult));
        }
    }


    //
    // Set syncapp flag
    //

    if (lpProfile->dwInternalFlags & PROFILE_RUN_SYNCAPP) {

        bRunSyncApp = TRUE;

        lResult = RegSetValueEx (hKey, SYNCAPP_REG_VALUE_NAME, 0, REG_DWORD,
                                 (LPBYTE) &bRunSyncApp, sizeof(bRunSyncApp));

        if (lResult != ERROR_SUCCESS) {
           DebugMsg((DM_WARNING, TEXT("UpgradeProfile: Failed to set syncapp flag. Error = %d"), lResult));
        }

        DebugMsg((DM_VERBOSE, TEXT("UpgradeProfile: Set syncapp flag to %d"), bRunSyncApp));
    }

    //
    // Close the registry key
    //

    RegCloseKey (hKey);



    if (bDoUserdiff) {

        //
        // Apply changes to user's hive that NT setup needs.
        //

        if (!ProcessUserDiff(lpProfile, dwBuildNumber)) {
            DebugMsg((DM_WARNING, TEXT("UpgradeProfile: ProcessUserDiff failed")));
        }
    }

    DebugMsg((DM_VERBOSE, TEXT("UpgradeProfile: Leaving Successfully")));

    return TRUE;

}

//*************************************************************
//
//  IsUserAGuest()
//
//  Purpose:    Determines if the user is a member of the guest group.
//
//  Parameters: lpProfile   -   Profile Information
//
//  Return:     TRUE if user is a guest
//              FALSE if not
//  Comments:
//
//  History:    Date        Author     Comment
//              7/25/95     ericflo    Created
//
//*************************************************************

BOOL IsUserAGuest(LPPROFILE lpProfile)
{
    SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
    NTSTATUS Status;
    ULONG InfoLength;
    PTOKEN_GROUPS TokenGroupList;
    ULONG GroupIndex;
    BOOL FoundGuests;
    PSID GuestsDomainSid;


    //
    // Create Guests domain sid.
    //


    Status = RtlAllocateAndInitializeSid(
               &authNT,
               2,
               SECURITY_BUILTIN_DOMAIN_RID,
               DOMAIN_ALIAS_RID_GUESTS,
               0, 0, 0, 0, 0, 0,
               &GuestsDomainSid
               );

    //
    // Test if user is in the Guests domain
    //

    //
    // Get a list of groups in the token
    //

    Status = NtQueryInformationToken(
                 lpProfile->hToken,        // Handle
                 TokenGroups,              // TokenInformationClass
                 NULL,                     // TokenInformation
                 0,                        // TokenInformationLength
                 &InfoLength               // ReturnLength
                 );

    if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) {

        DebugMsg((DM_WARNING, TEXT("IsUserAGuest:  Failed to get group info for guests token, status = 0x%x"), Status));
        RtlFreeSid(GuestsDomainSid);
        return FALSE;
    }


    TokenGroupList = GlobalAlloc(GPTR, InfoLength);

    if (TokenGroupList == NULL) {
        DebugMsg((DM_WARNING, TEXT("IsUserAGuest:  Unable to allocate memory for token groups")));
        RtlFreeSid(GuestsDomainSid);
        return FALSE;
    }

    Status = NtQueryInformationToken(
                 lpProfile->hToken,        // Handle
                 TokenGroups,              // TokenInformationClass
                 TokenGroupList,           // TokenInformation
                 InfoLength,               // TokenInformationLength
                 &InfoLength               // ReturnLength
                 );

    if (!NT_SUCCESS(Status)) {
        DebugMsg((DM_WARNING, TEXT("IsUserAGuest:  Failed to query groups for guests token, status = 0x%x"), Status));
        GlobalFree(TokenGroupList);
        RtlFreeSid(GuestsDomainSid);
        return FALSE;
    }


    //
    // Search group list for guests alias
    //

    FoundGuests = FALSE;

    for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) {

        if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, GuestsDomainSid)) {
            FoundGuests = TRUE;
            break;
        }
    }

    //
    // Tidy up
    //

    GlobalFree(TokenGroupList);
    RtlFreeSid(GuestsDomainSid);

    return(FoundGuests);
}

//*************************************************************
//
//  IsUserAnAdminMember()
//
//  Purpose:    Determines if the user is a member of the administrators group.
//
//  Parameters: lpProfile   -   Profile Information
//
//  Return:     TRUE if user is a admin
//              FALSE if not
//  Comments:
//
//  History:    Date        Author     Comment
//              7/25/95     ericflo    Created
//
//*************************************************************

BOOL IsUserAnAdminMember(LPPROFILE lpProfile)
{
    SID_IDENTIFIER_AUTHORITY authNT = SECURITY_NT_AUTHORITY;
    NTSTATUS Status;
    ULONG InfoLength;
    PTOKEN_GROUPS TokenGroupList;
    ULONG GroupIndex;
    BOOL FoundAdmins;
    PSID AdminsDomainSid;


    //
    // Create Admins domain sid.
    //


    Status = RtlAllocateAndInitializeSid(
               &authNT,
               2,
               SECURITY_BUILTIN_DOMAIN_RID,
               DOMAIN_ALIAS_RID_ADMINS,
               0, 0, 0, 0, 0, 0,
               &AdminsDomainSid
               );

    //
    // Test if user is in the Admins domain
    //

    //
    // Get a list of groups in the token
    //

    Status = NtQueryInformationToken(
                 lpProfile->hToken,        // Handle
                 TokenGroups,              // TokenInformationClass
                 NULL,                     // TokenInformation
                 0,                        // TokenInformationLength
                 &InfoLength               // ReturnLength
                 );

    if ((Status != STATUS_SUCCESS) && (Status != STATUS_BUFFER_TOO_SMALL)) {

        DebugMsg((DM_WARNING, TEXT("IsUserAnAdminMember:  Failed to get group info for Admins token, status = 0x%x"), Status));
        RtlFreeSid(AdminsDomainSid);
        return FALSE;
    }


    TokenGroupList = GlobalAlloc(GPTR, InfoLength);

    if (TokenGroupList == NULL) {
        DebugMsg((DM_WARNING, TEXT("IsUserAnAdminMember:  Unable to allocate memory for token groups")));
        RtlFreeSid(AdminsDomainSid);
        return FALSE;
    }

    Status = NtQueryInformationToken(
                 lpProfile->hToken,        // Handle
                 TokenGroups,              // TokenInformationClass
                 TokenGroupList,           // TokenInformation
                 InfoLength,               // TokenInformationLength
                 &InfoLength               // ReturnLength
                 );

    if (!NT_SUCCESS(Status)) {
        DebugMsg((DM_WARNING, TEXT("IsUserAnAdminMember:  Failed to query groups for Admins token, status = 0x%x"), Status));
        GlobalFree(TokenGroupList);
        RtlFreeSid(AdminsDomainSid);
        return FALSE;
    }


    //
    // Search group list for Admins alias
    //

    FoundAdmins = FALSE;

    for (GroupIndex=0; GroupIndex < TokenGroupList->GroupCount; GroupIndex++ ) {

        if (RtlEqualSid(TokenGroupList->Groups[GroupIndex].Sid, AdminsDomainSid)) {
            FoundAdmins = TRUE;
            break;
        }
    }

    //
    // Tidy up
    //

    GlobalFree(TokenGroupList);
    RtlFreeSid(AdminsDomainSid);

    return(FoundAdmins);
}

//*************************************************************
//
//  SetProfileTime()
//
//  Purpose:    Sets the timestamp on the remote profile and
//              local profile to be the same regardless of the
//              file system type being used.
//
//  Parameters: lpProfile   -   Profile Information
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              9/25/95     ericflo    Ported
//
//*************************************************************

BOOL SetProfileTime(LPPROFILE lpProfile)
{
    HANDLE hFileCentral;
    HANDLE hFileLocal;
    FILETIME ft;
    TCHAR szProfile[MAX_PATH];
    LPTSTR lpEnd;


    //
    // Impersonate the user
    //

    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("SetProfileTime: Failed to impersonate user")));
        return FALSE;
    }


    //
    // Create the central filename
    //

    lstrcpy (szProfile, lpProfile->szCentralProfile);
    lpEnd = CheckSlash (szProfile);

    if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
        lstrcpy (lpEnd, c_szNTUserMan);
    } else {
        lstrcpy (lpEnd, c_szNTUserDat);
    }


    hFileCentral = CreateFile(szProfile,
                              GENERIC_READ | GENERIC_WRITE,
                              FILE_SHARE_READ, NULL,
                              OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFileCentral == INVALID_HANDLE_VALUE) {
        DebugMsg((DM_WARNING, TEXT("SetProfileTime:  couldn't open central profile <%s>, error = %d"),
                 szProfile, GetLastError()));
        if (!RevertToSelf()) {
            DebugMsg((DM_WARNING, TEXT("SetProfileTime: Failed to revert to self")));
        }
        return FALSE;

    } else {

        if (!GetFileTime(hFileCentral, NULL, NULL, &ft)) {
            DebugMsg((DM_WARNING, TEXT("SetProfileTime:  couldn't get time of central profile, error = %d"), GetLastError()));
        }
    }

    //
    // Revert to being 'ourself'
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("SetProfileTime: Failed to revert to self")));
    }


    //
    // Create the local filename
    //

    lstrcpy (szProfile, lpProfile->szLocalProfile);
    lpEnd = CheckSlash (szProfile);

    if (lpProfile->dwInternalFlags & PROFILE_MANDATORY) {
        lstrcpy (lpEnd, c_szNTUserMan);
    } else {
        lstrcpy (lpEnd, c_szNTUserDat);
    }


    hFileLocal = CreateFile(szProfile,
                            GENERIC_READ | GENERIC_WRITE,
                            FILE_SHARE_READ, NULL,
                            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    if (hFileLocal == INVALID_HANDLE_VALUE) {

        DebugMsg((DM_WARNING, TEXT("SetProfileTime:  couldn't open local profile <%s>, error = %d"),
                 szProfile, GetLastError()));

    } else {

        if (!SetFileTime(hFileLocal, NULL, NULL, &ft)) {
            DebugMsg((DM_WARNING, TEXT("SetProfileTime: couldn't set time on local profile, error = %d"), GetLastError()));
        }
        if (!GetFileTime(hFileLocal, NULL, NULL, &ft)) {
            DebugMsg((DM_WARNING, TEXT("SetProfileTime:  couldn't get time on local profile, error = %d"), GetLastError()));
        }
        CloseHandle(hFileLocal);
    }

    //
    // Reset time of central profile in case of discrepencies in
    // times of different file systems.
    //

    //
    // Impersonate the user
    //

    if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
        DebugMsg((DM_WARNING, TEXT("SetProfileTime: Failed to impersonate user")));
        return FALSE;
    }


    //
    // Set the time on the central profile
    //
    if (hFileCentral != INVALID_HANDLE_VALUE) {
        if (!SetFileTime(hFileCentral, NULL, NULL, &ft)) {
             DebugMsg((DM_WARNING, TEXT("SetProfileTime:  couldn't set time on local profile, error = %d"), GetLastError()));
        }
        CloseHandle(hFileCentral);
    }

    //
    // Revert to being 'ourself'
    //

    if (!RevertToSelf()) {
        DebugMsg((DM_WARNING, TEXT("SetProfileTime: Failed to revert to self")));
    }

    return TRUE;
}

//*************************************************************
//
//  IsCacheDeleted()
//
//  Purpose:    Determines if the locally cached copy of the
//              roaming profile should be deleted.
//
//  Parameters: void
//
//  Return:     TRUE if local cache should be deleted
//              FALSE if not
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/28/96     ericflo    Created
//
//*************************************************************

BOOL IsCacheDeleted (void)
{
    BOOL bRetVal = FALSE;
    DWORD dwSize, dwType;
    HKEY hKey;

    //
    // Open the winlogon registry key
    //

    if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                      WINLOGON_KEY,
                      0,
                      KEY_READ,
                      &hKey) == ERROR_SUCCESS) {

        //
        // Check for the flag.
        //

        dwSize = sizeof(BOOL);
        RegQueryValueEx (hKey,
                         DELETE_ROAMING_CACHE,
                         NULL,
                         &dwType,
                         (LPBYTE) &bRetVal,
                         &dwSize);

        RegCloseKey (hKey);
    }

    return bRetVal;
}

//*************************************************************
//
//  UnloadUserProfile()
//
//  Purpose:    Unloads the user's profile.
//
//  Parameters: hToken    -   User's token
//              hProfile  -   Profile handle created in LoadUserProfile
//
//  Return:     TRUE if successful
//              FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              6/7/95      ericflo    Created
//
//*************************************************************

BOOL WINAPI UnloadUserProfile (HANDLE hToken, HANDLE hProfile)
{
    LPPROFILE lpProfile = NULL;
    LPTSTR lpSidString = NULL;
    LONG err, IgnoreError;
    BOOL bRet, bRetVal = FALSE;
    HANDLE hEvent = NULL;
    TCHAR szEventName[MAX_PATH];
    DWORD dwResult;
    SECURITY_DESCRIPTOR sd;
    SECURITY_ATTRIBUTES sa;


    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile: Entering, hProfile = <0x%x>"),
             hProfile));



    //
    // Check Parameters
    //

    if (!hProfile) {
        DebugMsg((DM_WARNING, TEXT("UnloadUserProfile: received a NULL hProfile.")));
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }


    //
    // Load profile information
    //

    lpProfile = LoadProfileInfo(hToken);

    if (!lpProfile) {
        RegCloseKey((HKEY)hProfile);
        goto Exit;
    }


    //
    // Restore the hKeyCurrentUser parameter
    //

    lpProfile->hKeyCurrentUser = (HKEY) hProfile;


    //
    // Get the Sid string for the current user
    //

    lpSidString = GetSidString(lpProfile->hToken);

    if (!lpSidString) {
        DebugMsg((DM_WARNING, TEXT("UnloadUserProfile: Failed to get sid string for user")));
        goto Exit;
    }


    //
    // Create an event to prevent multiple threads/process from trying to
    // unload the profile at the same time.
    //

    wsprintf (szEventName, TEXT("userenv: %s"), lpSidString);
    CharLower (szEventName);

    InitializeSecurityDescriptor( &sd, SECURITY_DESCRIPTOR_REVISION );

    SetSecurityDescriptorDacl (
                    &sd,
                    TRUE,                           // Dacl present
                    NULL,                           // NULL Dacl
                    FALSE                           // Not defaulted
                    );

    sa.lpSecurityDescriptor = &sd;
    sa.bInheritHandle = FALSE;
    sa.nLength = sizeof( sa );

    hEvent = CreateEvent ( &sa, TRUE, TRUE, szEventName);

    if (!hEvent) {

        if ( GetLastError() == ERROR_INVALID_HANDLE )
        {
            hEvent = OpenEvent( EVENT_MODIFY_STATE | SYNCHRONIZE, FALSE, szEventName );
        }

        if ( !hEvent )
        {
           DebugMsg((DM_WARNING, TEXT("UnloadUserProfile: Failed to create event %s.  Error = %d."),
              szEventName, GetLastError()));
           goto Exit;

        }
    }


    if ((WaitForSingleObject (hEvent, INFINITE) == WAIT_FAILED)) {
        DebugMsg((DM_WARNING, TEXT("UnloadUserProfile: Failed to wait on the event.  Error = %d."),
                  GetLastError()));
        goto Exit;
    }

    //
    // This will clear the event so other threads/process will have to
    // wait in the WaitForSingleObject call.
    //

    ResetEvent (hEvent);



    //
    // Flush out the profile which will also sync the log.
    //

    err = RegFlushKey(lpProfile->hKeyCurrentUser);
    if (err != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("UnloadUserProfile:  Failed to flush the current user key, error = %d"), err));
    }


    //
    // Close the current user key that was opened in LoadUserProfile.
    //

    err = RegCloseKey(lpProfile->hKeyCurrentUser);
    if (err != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("UnloadUserProfile:  Failed to close the current user key, error = %d"), err));
    }


    //
    // If this is a mandatory or a guest profile, unload it now.
    // Guest profiles are always deleted so one guest can't see
    // the profile of a previous guest.
    //

    if ((lpProfile->dwInternalFlags & PROFILE_MANDATORY) ||
        (lpProfile->dwInternalFlags & PROFILE_GUEST_USER)) {

        err = MyRegUnLoadKey(HKEY_USERS, lpSidString);

        if (!err) {
            DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile:  Didn't unload the user profile because of error = %d"), GetLastError()));
            bRetVal = TRUE;
            goto Exit;

        } else {
            DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile:  Succesfully unloaded mandatory/guest profile")));
        }

        IgnoreError = RegFlushKey(HKEY_USERS);
        if (IgnoreError != ERROR_SUCCESS) {
            DebugMsg((DM_WARNING, TEXT("UnloadUserProfile:  Failed to flush HKEY_USERS, error = %d"), IgnoreError));
        }

        if (IsCacheDeleted() || (lpProfile->dwInternalFlags & PROFILE_GUEST_USER)) {

            //
            // Delete the profile
            //

            if (!DeleteProfile (lpSidString, lpProfile->szLocalProfile, FALSE)) {
                DebugMsg((DM_WARNING, TEXT("UnloadUserProfile:  DeleteProfileDirectory returned false.  Error = %d"), GetLastError()));
            }
        }

        if (err) {
            bRetVal = TRUE;
        }

        goto Exit;
    }



    //
    //  Unload the profile
    //

    err = MyRegUnLoadKey(HKEY_USERS, lpSidString);

    if (!err) {
        DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile:  Didn't unload user profile <err = %d>"), GetLastError()));
        bRetVal = TRUE;
        goto Exit;

    } else {
        DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile:  Succesfully unloaded profile")));
    }



    //
    // Copy local profileimage to remote profilepath
    //

    if ( ((lpProfile->dwInternalFlags & PROFILE_UPDATE_CENTRAL) ||
          (lpProfile->dwInternalFlags & PROFILE_NEW_CENTRAL)) ) {

        if ((lpProfile->dwUserPreference != USERINFO_LOCAL) &&
            !(lpProfile->dwInternalFlags & PROFILE_SLOW_LINK)) {

            DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile:  Copying profile back to %s"),
                            lpProfile->szCentralProfile));

            //
            // Impersonate the user
            //

            if (!ImpersonateLoggedOnUser(lpProfile->hToken)) {
                DebugMsg((DM_WARNING, TEXT("UnloadUserProfile: Failed to impersonate user")));
                goto Exit;
            }


            bRet = CopyProfileDirectory (lpProfile->szLocalProfile,
                                         lpProfile->szCentralProfile,
                                         CPD_IGNORECOPYERRORS |
                                         CPD_COPYIFDIFFERENT  |
                                         CPD_SYNCHRONIZE);

            if (!RevertToSelf()) {
                DebugMsg((DM_WARNING, TEXT("UnloadUserProfile: Failed to revert to self")));
            }



            //
            // Check return value
            //

            if (!bRet) {

                DebugMsg((DM_WARNING, TEXT("UnloadUserProfile:  CopyProfileDirectory returned FALSE.  Error = %d"), GetLastError()));
                ReportError(lpProfile->dwFlags, IDS_CENTRAL_UPDATE_FAILED, GetLastError());
                goto Exit;
            }

            //
            // The profile is copied, now we want to make sure the timestamp on
            // both the remote profile and the local copy are the same, so we don't
            // ask the user to update when it's not necessary.
            //

            SetProfileTime(lpProfile);

            if (IsCacheDeleted()) {
                if (!DeleteProfile (lpSidString, lpProfile->szLocalProfile, FALSE)) {
                    DebugMsg((DM_WARNING, TEXT("UnloadUserProfile:  DeleteProfileDirectory returned false (2).  Error = %d"), GetLastError()));
                }
            }
        }
    }

    //
    // Success
    //

    bRetVal = TRUE;


Exit:


    if (hEvent) {

        //
        // This will set the event so other threads/process can continue.
        //

        SetEvent (hEvent);
        CloseHandle (hEvent);
    }


    if (lpSidString) {
        DeleteSidString(lpSidString);
    }


    if (lpProfile) {
        LocalFree (lpProfile);
    }


    //
    // Verbose output
    //

    DebugMsg((DM_VERBOSE, TEXT("UnloadUserProfile: Leaving with a return value of %d"), bRetVal));


    return bRetVal;
}


//*************************************************************
//
//  SaveProfileInfo()
//
//  Purpose:    Saves key parts of the lpProfile structure
//              in the registry for UnloadUserProfile to use.
//
//  Parameters: lpProfile   -   Profile information
//
//  Return:     (BOOL) TRUE if successful
//                     FALSE if an error occurs
//
//  Comments:
//
//  History:    Date        Author     Comment
//              12/4/95     ericflo    Created
//
//*************************************************************

BOOL SaveProfileInfo(LPPROFILE lpProfile)
{
    LPTSTR SidString, lpEnd;
    TCHAR LocalProfileKey[MAX_PATH];
    LONG lResult;
    HKEY hKey;
    DWORD dwType, dwSize, dwCount, dwDisp;


    //
    // Get the Sid string for the user
    //

    SidString = GetSidString(lpProfile->hToken);
    if (!SidString) {
        DebugMsg((DM_WARNING, TEXT("SaveProfileInfo:  Failed to get sid string for user")));
        return FALSE;
    }


    //
    // Open the profile mapping
    //

    lstrcpy(LocalProfileKey, PROFILE_LIST_PATH);
    lpEnd = CheckSlash (LocalProfileKey);
    lstrcpy(lpEnd, SidString);

    lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, LocalProfileKey, 0, 0, 0,
                             KEY_READ | KEY_WRITE, NULL, &hKey, &dwDisp);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_VERBOSE, TEXT("SaveProfileInfo:  Failed to open profile mapping key with error %d"), lResult));
        return FALSE;
    }


    //
    // Save the flags
    //

    lResult = RegSetValueEx (hKey,
                             PROFILE_FLAGS,
                             0,
                             REG_DWORD,
                             (LPBYTE) &lpProfile->dwFlags,
                             sizeof(DWORD));

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_VERBOSE, TEXT("SaveProfileInfo:  Failed to save flags with error %d"), lResult));
    }


    //
    // Save the internal flags
    //

    lResult = RegSetValueEx (hKey,
                             PROFILE_STATE,
                             0,
                             REG_DWORD,
                             (LPBYTE) &lpProfile->dwInternalFlags,
                             sizeof(DWORD));

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_VERBOSE, TEXT("SaveProfileInfo:  Failed to save flags2 with error %d"), lResult));
    }


    //
    // Save the central profile path
    //

    lResult = RegSetValueEx (hKey,
                             PROFILE_CENTRAL_PROFILE,
                             0,
                             REG_SZ,
                             (LPBYTE) &lpProfile->szCentralProfile,
                             (lstrlen(lpProfile->szCentralProfile) + 1) * sizeof(TCHAR));

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_VERBOSE, TEXT("SaveProfileInfo:  Failed to save central profile with error %d"), lResult));
    }


    RegCloseKey (hKey);


    DeleteSidString(SidString);


    return(TRUE);
}


//*************************************************************
//
//  LoadProfileInfo()
//
//  Purpose:    Loads key parts of the lpProfile structure
//              in the registry for UnloadUserProfile to use.
//
//  Parameters: hToken   -   User's token
//
//  Return:     pointer to lpProfile is successful
//              NULL if not
//
//  Comments:   This function doesn't re-initialize all of the
//              fields in the PROFILE structure.
//
//  History:    Date        Author     Comment
//              12/5/95     ericflo    Created
//
//*************************************************************

LPPROFILE LoadProfileInfo(HANDLE hToken)
{
    LPPROFILE lpProfile;
    LPTSTR SidString, lpEnd;
    TCHAR szBuffer[MAX_PATH];
    LONG lResult;
    HKEY hKey;
    DWORD dwType, dwSize;


    //
    // Allocate an internal Profile structure to work with.
    //

    lpProfile = (LPPROFILE) LocalAlloc (LPTR, sizeof(PROFILE));

    if (!lpProfile) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo: Failed to allocate memory")));
        return NULL;
    }


    //
    // Save the data passed in.
    //

    lpProfile->hToken = hToken;



    //
    // Get the Sid string for the user
    //

    SidString = GetSidString(hToken);
    if (!SidString) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo:  Failed to get sid string for user")));
        LocalFree (lpProfile);
        return NULL;
    }


    //
    // Open the profile mapping
    //

    lstrcpy(szBuffer, PROFILE_LIST_PATH);
    lpEnd = CheckSlash (szBuffer);
    lstrcpy(lpEnd, SidString);

    lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuffer, 0,
                             KEY_READ, &hKey);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo:  Failed to open profile mapping key with error %d"), lResult));
        LocalFree (lpProfile);
        return NULL;
    }


    //
    // Query for the flags
    //

    dwSize = sizeof(DWORD);
    lResult = RegQueryValueEx (hKey,
                               PROFILE_FLAGS,
                               NULL,
                               &dwType,
                               (LPBYTE) &lpProfile->dwFlags,
                               &dwSize);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo:  Failed to query flags with error %d"), lResult));
        LocalFree (lpProfile);
        return NULL;
    }


    //
    // Query for the internal flags
    //

    dwSize = sizeof(DWORD);
    lResult = RegQueryValueEx (hKey,
                               PROFILE_STATE,
                               NULL,
                               &dwType,
                               (LPBYTE) &lpProfile->dwInternalFlags,
                               &dwSize);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo:  Failed to query internal flags with error %d"), lResult));
        LocalFree (lpProfile);
        return NULL;
    }


    //
    // Query for the user preference value
    //


    lpProfile->dwUserPreference = USERINFO_UNDEFINED;
    dwSize = sizeof(DWORD);

    RegQueryValueEx (hKey,
                     USER_PREFERENCE,
                     NULL,
                     &dwType,
                     (LPBYTE) &lpProfile->dwUserPreference,
                     &dwSize);



    //
    // Query for the central profile path
    //

    dwSize = MAX_PATH * 2;
    lResult = RegQueryValueEx (hKey,
                               PROFILE_CENTRAL_PROFILE,
                               NULL,
                               &dwType,
                               (LPBYTE) &lpProfile->szCentralProfile,
                               &dwSize);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo:  Failed to query central profile with error %d"), lResult));
        LocalFree (lpProfile);
        return NULL;
    }


    //
    // Query for the local profile path.  The local profile path
    // needs to be expanded so read it into the temporary buffer.
    //

    dwSize = MAX_PATH * 2;
    lResult = RegQueryValueEx (hKey,
                               PROFILE_IMAGE_VALUE_NAME,
                               NULL,
                               &dwType,
                               (LPBYTE) szBuffer,
                               &dwSize);

    if (lResult != ERROR_SUCCESS) {
        DebugMsg((DM_WARNING, TEXT("LoadProfileInfo:  Failed to query local profile with error %d"), lResult));
        LocalFree (lpProfile);
        return NULL;
    }


    //
    // Expand the local profile
    //

    ExpandEnvironmentStrings(szBuffer, lpProfile->szLocalProfile, MAX_PATH);



    RegCloseKey (hKey);


    DeleteSidString(SidString);

    return(lpProfile);
}

//*************************************************************
//
//  CheckForSlowLink()
//
//  Purpose:    Checks if the network connection is slow.
//
//  Parameters: lpProfile   -   Profile Information
//              dwTime      -   Time delta
//
//  Return:     TRUE if profile should be downloaded
//              FALSE if not (use local)
//
//  Comments:
//
//  History:    Date        Author     Comment
//              2/21/96     ericflo    Created
//
//*************************************************************

BOOL CheckForSlowLink(LPPROFILE lpProfile, DWORD dwTime)
{
    DWORD dwSlowTimeOut, dwSlowDlgTimeOut, dwSlowLinkDetectEnabled;
    DWORD dwType, dwSize;
    BOOL bRetVal;
    HKEY hKey;
    LONG lResult;


    //
    // If the user doesn't want pop-up's, then they always
    // get their profile downloaded.
    //

    if (lpProfile->dwFlags & PI_NOUI) {
        return TRUE;
    }

    //
    // If the User Preferences states to always use the local
    // profile then we can exit now with true.  The profile
    // won't actually be downloaded.  In RestoreUserProfile,
    // this will be filtered out, and only the local will be used.
    //

    if (lpProfile->dwUserPreference == USERINFO_LOCAL) {
        return TRUE;
    }


    //
    // Get the slow link detection flag, slow link timeout,
    // and dialog box timeout values.
    //

    dwSlowTimeOut = SLOW_LINK_TIMEOUT;
    dwSlowDlgTimeOut = PROFILE_DLG_TIMEOUT;
    dwSlowLinkDetectEnabled = 1;

    lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                           WINLOGON_KEY,
                           0,
                           KEY_READ,
                           &hKey);

    if (lResult == ERROR_SUCCESS) {

        dwSize = sizeof(DWORD);
        RegQueryValueEx (hKey,
                         TEXT("SlowLinkDetectEnabled"),
                         NULL,
                         &dwType,
                         (LPBYTE) &dwSlowLinkDetectEnabled,
                         &dwSize);


        dwSize = sizeof(DWORD);
        RegQueryValueEx (hKey,
                         TEXT("SlowLinkTimeOut"),
                         NULL,
                         &dwType,
                         (LPBYTE) &dwSlowTimeOut,
                         &dwSize);

        dwSize = sizeof(DWORD);
        RegQueryValueEx (hKey,
                         TEXT("ProfileDlgTimeOut"),
                         NULL,
                         &dwType,
                         (LPBYTE) &dwSlowDlgTimeOut,
                         &dwSize);


        RegCloseKey (hKey);
    }


    //
    // If slow link detection is disabled, then always download
    // the profile.
    //

    if (!dwSlowLinkDetectEnabled) {
        return TRUE;
    }


    //
    // If the delta time is less that the timeout time, then it
    // is ok to download their profile (fast enough net connection).
    //

    if (dwTime < dwSlowTimeOut) {
        return TRUE;
    }


    //
    // If the User Preferences states to always use the local
    // profile on slow links, then we can exit now with false.
    //

    if (lpProfile->dwUserPreference == USERINFO_LOCAL_SLOW_LINK) {
        lpProfile->dwInternalFlags |= PROFILE_SLOW_LINK;
        return FALSE;
    }


    //
    // Display the slow link dialog
    //
    // If someone sets the dialog box timeout to 0, then we
    // don't want to prompt the user.  Just force the profile
    // to download.
    //

    if (dwSlowDlgTimeOut > 0) {

        bRetVal = DialogBoxParam (g_hDllInstance, MAKEINTRESOURCE(IDD_SLOW_LINK),
                                  NULL, SlowLinkDlgProc, dwSlowDlgTimeOut);

        if (!bRetVal) {
            lpProfile->dwInternalFlags |= PROFILE_SLOW_LINK;
        }

        return bRetVal;
    }

    return TRUE;
}


//*************************************************************
//
//  SlowLinkDlgProc()
//
//  Purpose:    Dialog box procedure for the slow link dialog
//
//  Parameters: hDlg    -   handle to the dialog box
//              uMsg    -   window message
//              wParam  -   wParam
//              lParam  -   lParam
//
//  Return:     TRUE if message was processed
//              FALSE if not
//
//  Comments:
//
//  History:    Date        Author     Comment
//              2/13/96     ericflo    Created
//
//*************************************************************

BOOL APIENTRY SlowLinkDlgProc (HWND hDlg, UINT uMsg,
                               WPARAM wParam, LPARAM lParam)
{
    TCHAR szBuffer[10];
    static DWORD dwSlowLinkTime;

    switch (uMsg) {

        case WM_INITDIALOG:
           CenterWindow (hDlg);
           dwSlowLinkTime = (DWORD) lParam;
           wsprintf (szBuffer, TEXT("%d"), dwSlowLinkTime);
           SetDlgItemText (hDlg, IDC_TIMEOUT, szBuffer);
           SetTimer (hDlg, 1, 1000, NULL);
           return TRUE;

        case WM_TIMER:

           if (dwSlowLinkTime >= 1) {

               dwSlowLinkTime--;
               wsprintf (szBuffer, TEXT("%d"), dwSlowLinkTime);
               SetDlgItemText (hDlg, IDC_TIMEOUT, szBuffer);

           } else {

               //
               // Time's up.  Download the profile.
               //

               PostMessage (hDlg, WM_COMMAND, IDC_DOWNLOAD, 0);
           }
           break;

        case WM_COMMAND:

          switch (LOWORD(wParam)) {

              case IDC_DOWNLOAD:
              case IDC_LOCAL:
              case IDCANCEL:

                  //
                  // Nothing to do.  Save the state and return.
                  //

                  KillTimer (hDlg, 1);

                  //
                  // Return TRUE to download the profile,
                  // FALSE to use the local profile
                  //

                  EndDialog(hDlg, ((LOWORD(wParam) == IDC_LOCAL) ? FALSE : TRUE));
                  break;

              default:
                  break;

          }
          break;

    }

    return FALSE;
}
//*************************************************************
//
//  GetUserPreferenceValue()
//
//  Purpose:    Gets the User Preference flags
//
//  Parameters: hToken  -   User's token
//
//  Return:     Value
//
//  Comments:
//
//  History:    Date        Author     Comment
//              2/22/96     ericflo    Created
//
//*************************************************************

DWORD GetUserPreferenceValue(HANDLE hToken)
{
    TCHAR LocalProfileKey[MAX_PATH];
    DWORD RegErr, dwType, dwSize, dwRetVal = USERINFO_UNDEFINED;
    LPTSTR lpEnd;
    LPTSTR SidString;
    HKEY hkeyProfile;


    SidString = GetSidString(hToken);
    if (SidString != NULL) {

        //
        // Query for the UserPreference value
        //

        lstrcpy(LocalProfileKey, PROFILE_LIST_PATH);
        lpEnd = CheckSlash (LocalProfileKey);
        lstrcpy(lpEnd, SidString);

        RegErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                              LocalProfileKey,
                              0,
                              KEY_READ,
                              &hkeyProfile);


        if (RegErr == ERROR_SUCCESS) {

            dwSize = sizeof(dwRetVal);
            RegQueryValueEx(hkeyProfile,
                            USER_PREFERENCE,
                            NULL,
                            &dwType,
                            (LPBYTE) &dwRetVal,
                            &dwSize);

            RegCloseKey (hkeyProfile);
        }

        DeleteSidString(SidString);
    }

    return dwRetVal;
}

//*************************************************************
//
//  ChooseProfileDlgProc()
//
//  Purpose:    Dialog box procedure for the choose profile dialog
//
//  Parameters: hDlg    -   handle to the dialog box
//              uMsg    -   window message
//              wParam  -   wParam
//              lParam  -   lParam
//
//  Return:     TRUE if message was processed
//              FALSE if not
//
//  Comments:
//
//  History:    Date        Author     Comment
//              2/23/96     ericflo    Created
//
//*************************************************************

BOOL APIENTRY ChooseProfileDlgProc (HWND hDlg, UINT uMsg,
                               WPARAM wParam, LPARAM lParam)
{
    TCHAR szBuffer[10];
    static DWORD dwChooseProfileTime;

    switch (uMsg) {

        case WM_INITDIALOG:
           CenterWindow (hDlg);
           dwChooseProfileTime = (DWORD) lParam;
           wsprintf (szBuffer, TEXT("%d"), dwChooseProfileTime);
           SetDlgItemText (hDlg, IDC_TIMEOUT, szBuffer);
           SetTimer (hDlg, 1, 1000, NULL);
           return TRUE;

        case WM_TIMER:

           if (dwChooseProfileTime >= 1) {

               dwChooseProfileTime--;
               wsprintf (szBuffer, TEXT("%d"), dwChooseProfileTime);
               SetDlgItemText (hDlg, IDC_TIMEOUT, szBuffer);

           } else {

               //
               // Time's up.  Use the local profile.
               //

               PostMessage (hDlg, WM_COMMAND, IDC_CP_YES, 0);
           }
           break;

        case WM_COMMAND:

          switch (LOWORD(wParam)) {

              case IDC_CP_YES:
              case IDC_CP_NO:
              case IDCANCEL:

                  //
                  // Nothing to do.  Save the state and return.
                  //

                  KillTimer (hDlg, 1);

                  //
                  // Return IDYES to use the local profile
                  // IDNO to download the central profile
                  //

                  EndDialog(hDlg, ((LOWORD(wParam) == IDC_CP_NO) ? IDNO : IDYES));
                  break;

              default:
                  break;

          }
          break;

    }

    return FALSE;
}