//
// ident.cpp - implementation of CIdentity class
//
#include "private.h"
#include "multiusr.h"
#include "multiui.h"
#include "strconst.h"
#include "resource.h"
#include "mluisup.h"

extern HINSTANCE g_hInst;
BOOL        g_fReleasedMutex = true;


//
// Constructor / destructor
//
CUserIdentityManager::CUserIdentityManager()
{
    m_cRef = 1;
    m_fWndRegistered = FALSE;
    m_hwnd = NULL;
    m_pAdviseRegistry = NULL;
    InitializeCriticalSection(&m_rCritSect);
    DllAddRef();
}

CUserIdentityManager::~CUserIdentityManager()
{
    if (m_pAdviseRegistry)
        m_pAdviseRegistry->Release();
    DeleteCriticalSection(&m_rCritSect);
    DllRelease();
}


//
// IUnknown members
//
STDMETHODIMP CUserIdentityManager::QueryInterface(
    REFIID riid, void **ppv)
{
    if (NULL == ppv)
    {
        return E_INVALIDARG;
    }
    
    *ppv=NULL;

    // Validate requested interface
    if (IID_IUnknown == riid)
    {
        *ppv = (IUserIdentityManager *)this;
    }
    else if (IID_IUserIdentityManager == riid)
    {
        *ppv = (IUserIdentityManager *)this;
    }
    else if (IID_IConnectionPoint == riid)
    {
        *ppv = (IConnectionPoint *)this;
    }
    else if (IID_IPrivateIdentityManager == riid)
    {
        *ppv = (IPrivateIdentityManager *)this;
    }
    else if (IID_IPrivateIdentityManager2 == riid)
    {
        *ppv = (IPrivateIdentityManager2 *)this;
    }

    // Addref through the interface
    if (NULL != *ppv) {
        ((LPUNKNOWN)*ppv)->AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CUserIdentityManager::AddRef()
{
    return ++m_cRef;
}

STDMETHODIMP_(ULONG) CUserIdentityManager::Release()
{
    if (0L != --m_cRef)
        return m_cRef;

    delete this;
    return 0L;
}


STDMETHODIMP CUserIdentityManager::CreateIdentity(WCHAR *pszName, IUserIdentity **ppIdentity)
{
    return CreateIdentity2(pszName, NULL, ppIdentity);
}

STDMETHODIMP CUserIdentityManager::CreateIdentity2(WCHAR *pszName, WCHAR *pszPassword, IUserIdentity **ppIdentity)
{
    CUserIdentity *pIdentity;
    HRESULT  hr;
    TCHAR   szName[CCH_IDENTITY_NAME_MAX_LENGTH+1];

    *ppIdentity = NULL;

    if (MU_IdentitiesDisabled())
        return E_IDENTITIES_DISABLED;

    if (WideCharToMultiByte(CP_ACP, 0, pszName, -1, szName, CCH_IDENTITY_NAME_MAX_LENGTH, NULL, NULL) == 0)
        return GetLastError();

    if (MU_UsernameExists(szName))
        return E_IDENTITY_EXISTS;

    pIdentity = new CUserIdentity;
    
    Assert(pIdentity);

    if (!pIdentity)
        return E_OUTOFMEMORY;

    hr = pIdentity->SetName(pszName);
    
    if (SUCCEEDED(hr))
    {
        if (pszPassword)
        {
            hr = pIdentity->SetPassword(pszPassword);
        }
    }

    if (SUCCEEDED(hr))
    {
        *ppIdentity = pIdentity;
    }
    else
    {
        pIdentity->Release();
    }

    PostMessage(HWND_BROADCAST, WM_IDENTITY_INFO_CHANGED, 0, IIC_IDENTITY_ADDED);

    return hr;
}

STDMETHODIMP CUserIdentityManager::ConfirmPassword(GUID *uidCookie, WCHAR *pszPassword)
{
    TCHAR           szPwd[CCH_USERPASSWORD_MAX_LENGTH+1];
    HRESULT         hr = E_FAIL;
    USERINFO        userInfo;

    if (WideCharToMultiByte(CP_ACP, 0, pszPassword, -1, szPwd, CCH_USERPASSWORD_MAX_LENGTH, NULL, NULL) == 0)
        return E_FAIL;

    if (MU_GetUserInfo(uidCookie, &userInfo))
    {
        if (userInfo.fPasswordValid)
        {
            if (!userInfo.fUsePassword)
                userInfo.szPassword[0] = 0;

            if (lstrcmp(szPwd, userInfo.szPassword) == 0)
                hr = S_OK;
            else
                hr = E_FAIL;
        }
        else
        {
            hr = E_FAIL;
        }
    }

    return hr;
}

STDMETHODIMP CUserIdentityManager::DestroyIdentity(GUID *uidCookie)
{
    if (MU_IdentitiesDisabled())
        return E_IDENTITIES_DISABLED;
    
    return MU_DeleteUser(uidCookie);
}

STDMETHODIMP CUserIdentityManager::EnumIdentities(IEnumUserIdentity **ppEnumIdentity)
{
    CEnumUserIdentity   *pEnumIdentity;

    *ppEnumIdentity = NULL;

    pEnumIdentity = new CEnumUserIdentity;

    if (!pEnumIdentity)
        return E_OUTOFMEMORY;

    *ppEnumIdentity = pEnumIdentity;

    return S_OK;
}

STDMETHODIMP CUserIdentityManager::SetDefaultIdentity(GUID *puidCookie)
{
    if (MU_IdentitiesDisabled())
        return E_IDENTITIES_DISABLED;

    return MU_MakeDefaultUser(puidCookie);
}

STDMETHODIMP CUserIdentityManager::GetDefaultIdentity(GUID *puidCookie)
{
    if (MU_IdentitiesDisabled())
        return E_IDENTITIES_DISABLED;

    return MU_GetDefaultUserID(puidCookie) ? S_OK : S_FALSE;
}

STDMETHODIMP CUserIdentityManager::ManageIdentities(HWND hwndParent, DWORD dwFlags)
{
    TCHAR    szUsername[CCH_USERNAME_MAX_LENGTH+1];

    if (MU_IdentitiesDisabled())
        return E_IDENTITIES_DISABLED;
    
    *szUsername = 0;

    MU_ManageUsers(hwndParent, szUsername, dwFlags);
    
    // if the user created a new user and said they want to switch to them now,
    // we should do so.
    if (*szUsername)
    {
        BOOL        fGotUser;
        USERINFO    rUser;
        GUID        uidUserID;
        HRESULT     hr;

        fGotUser = MU_GetUserInfo(NULL, &rUser);
        if (!fGotUser)
        {
            *rUser.szUsername = 0;
            ZeroMemory(&rUser.uidUserID, sizeof(GUID));
        }
        MU_UsernameToUserId(szUsername, &uidUserID);

        if (FAILED(hr = _SwitchToUser(&rUser.uidUserID, &uidUserID)))
        {
            SetForegroundWindow(hwndParent);
            
            if (hr != E_USER_CANCELLED)
                MU_ShowErrorMessage(hwndParent, idsSwitchCancelled, idsSwitchCancelCaption);
        }
    }
    return S_OK;
}

STDMETHODIMP CUserIdentityManager::_PersistChangingIdentities()
{
    HRESULT hr = E_FAIL;
    HKEY hKeyIdentities = NULL;

    if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, c_szRegRoot, &hKeyIdentities))
    {
        goto exit;
    }

    if (ERROR_SUCCESS != RegSetValueEx(hKeyIdentities, c_szOutgoingID, 0, REG_BINARY, (LPBYTE)&g_uidOldUserId, sizeof(GUID)))
    {
        goto exit;
    }
    
    if (ERROR_SUCCESS != RegSetValueEx(hKeyIdentities, c_szIncomingID, 0, REG_BINARY, (LPBYTE)&g_uidNewUserId, sizeof(GUID)))
    {
        goto exit;
    }
    
    if (ERROR_SUCCESS != RegSetValueEx(hKeyIdentities, c_szChanging, 0, REG_BINARY, (LPBYTE)&g_fNotifyComplete, sizeof(g_fNotifyComplete)))
    {
        goto exit;
    }
    

    hr = S_OK;
exit:
    if (hKeyIdentities)
    {
        RegCloseKey(hKeyIdentities);
    }
    
    return hr;
}

STDMETHODIMP CUserIdentityManager::_LoadChangingIdentities()
{
    HRESULT hr = E_FAIL;
    HKEY hKeyIdentities = NULL;
    DWORD dwType, dwSize;

    if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, c_szRegRoot, &hKeyIdentities))
    {
        goto exit;
    }

    dwType = REG_BINARY;
    dwSize = sizeof(GUID);
    if (ERROR_SUCCESS != RegQueryValueEx(hKeyIdentities, c_szOutgoingID, 0, &dwType, (LPBYTE)&g_uidOldUserId, &dwSize))
    {
        goto exit;
    }
    
    dwSize = sizeof(GUID);
    if (ERROR_SUCCESS != RegQueryValueEx(hKeyIdentities, c_szIncomingID, 0, &dwType, (LPBYTE)&g_uidNewUserId, &dwSize))
    {
        goto exit;
    }

    dwSize = sizeof(g_fNotifyComplete);
    if (ERROR_SUCCESS != RegQueryValueEx(hKeyIdentities, c_szChanging, 0, &dwType, (LPBYTE)&g_fNotifyComplete, &dwSize))
    {
        goto exit;
    }


    hr = S_OK;
exit:
    if (FAILED(hr))
    {
        g_uidOldUserId = GUID_NULL;
        g_uidNewUserId = GUID_NULL;
        g_fNotifyComplete = TRUE;
    }
    
    if (hKeyIdentities)
    {
        RegCloseKey(hKeyIdentities);
    }
    
    return hr;
}

STDMETHODIMP CUserIdentityManager::ClearChangingIdentities()
{
    HRESULT hr = E_FAIL;
    HKEY hKeyIdentities = NULL;

    if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, c_szRegRoot, &hKeyIdentities))
    {
        goto exit;
    }

    RegDeleteValue(hKeyIdentities, c_szChanging);
    RegDeleteValue(hKeyIdentities, c_szIncomingID);
    RegDeleteValue(hKeyIdentities, c_szOutgoingID);    

    hr = S_OK;
    
exit:
    if (hKeyIdentities)
    {
        RegCloseKey(hKeyIdentities);
    }
    
    return hr;

}

STDMETHODIMP CUserIdentityManager::Logon(HWND hwndParent, DWORD dwFlags, IUserIdentity **ppIdentity)
{
    CUserIdentity *pIdentity;
    HRESULT     hr = E_FAIL;
    USERINFO    rUser;
    GUID        uidUserID, uidNewUserID;
    BOOL        fGotUser;
    TCHAR       szOldUsername[CCH_USERNAME_MAX_LENGTH+1], szLogoffName[CCH_USERNAME_MAX_LENGTH+1];
    TCHAR       szRes[MAX_PATH];

    // if identities are disabled, always return the default identity.
    // if they are forcing the UI, return an error, otherwise succeed and 
    // send the message back that identities are disabled.
    if (MU_IdentitiesDisabled())
    {
        if (!!(dwFlags & UIL_FORCE_UI))
            return E_IDENTITIES_DISABLED;

        hr = GetIdentityByCookie((GUID *)&UID_GIBC_DEFAULT_USER, ppIdentity);
        
        return (SUCCEEDED(hr) ? S_IDENTITIES_DISABLED : hr);
    }

    if (!g_hMutex)
        return E_UNEXPECTED;

    _LoadChangingIdentities();

    if (g_uidOldUserId != GUID_NULL || g_uidNewUserId != GUID_NULL)
    {
        // we are in the middle of a switch
        if (!g_fNotifyComplete)
        {
            // and we are not done checking to see if a switch is ok.
            if (!!(dwFlags & UIL_FORCE_UI))    //if its a force ui, then just fail.
                return E_IDENTITY_CHANGING;    

            //otherwise, we need to do something here, but since they could be
            //calling Login from the notifier proc, this could create a deadlock,
            //but returning either the old or the new could be wrong.  Return the
            //same error here unless we can come up with a better solution.
            return E_IDENTITY_CHANGING;
        }
    }

    DWORD dwWaitResult;
    dwWaitResult = WaitForSingleObject(g_hMutex, 5000); 
    g_fReleasedMutex = false;
    if (dwWaitResult == WAIT_TIMEOUT)
    {
        char    szMsg[255], szTitle[63];

        // someone else seems to have a login dialog up.  Notify the user
        // about this problem and bail.
        if (!!(dwFlags & UIL_FORCE_UI))
        {
            MLLoadStringA(idsSwitchInProgressSwitch, szMsg, ARRAYSIZE(szMsg));
            MLLoadStringA(idsSwitchIdentities, szTitle, ARRAYSIZE(szTitle));
        }
        else
        {
            MLLoadStringA(idsSwitchInProgressLaunch, szMsg, ARRAYSIZE(szMsg));
            MLLoadStringA(idsIdentityLogin, szTitle, ARRAYSIZE(szTitle));
        }

        MessageBox(hwndParent, szMsg, szTitle, MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL);

        return E_UNEXPECTED;
    }

    *ppIdentity = NULL;
    fGotUser = MU_GetUserInfo(NULL, &rUser);
    if (!fGotUser)
    {
        *rUser.szUsername = 0;
        ZeroMemory(&rUser.uidUserID, sizeof(GUID));
    }
    lstrcpy(szOldUsername, rUser.szUsername);

    // if we don't have to do the UI and there is a current 
    // user, then just return that identity
    if (!(dwFlags & UIL_FORCE_UI) && fGotUser)
    {
        pIdentity = new CUserIdentity;
        
        if (!pIdentity)
            hr = E_OUTOFMEMORY;

        if (pIdentity && SUCCEEDED(hr = pIdentity->InitFromUsername(rUser.szUsername)))
            *ppIdentity = pIdentity;
    }
    else
    {
        if (0 == *rUser.szUsername)
        {
            GUID    uidStart;

            MU_GetLoginOption(&uidStart);
            if (GUID_NULL != uidStart)
            {
                MU_GetUserInfo(&uidStart, &rUser);
                rUser.uidUserID = GUID_NULL;
        
            }
        }

        if (MU_Login(hwndParent, dwFlags, rUser.szUsername))
        {
            MLLoadStringA(idsLogoff, szLogoffName, sizeof(szLogoffName));
            if (lstrcmp(szLogoffName, rUser.szUsername) == 0)
            {
                MLLoadStringA(idsConfirmLogoff, szRes, sizeof(szRes));

                if (MessageBox(hwndParent, szRes, szLogoffName, MB_YESNO) == IDYES)
                {
                    ReleaseMutex(g_hMutex);
                    g_fReleasedMutex = true;
                    Logoff(hwndParent);
                }
            }
            else
            {
                pIdentity = new CUserIdentity;
                if (pIdentity)
                {
                    hr = pIdentity->InitFromUsername(rUser.szUsername);

                    if (SUCCEEDED(hr))
                    {
                        pIdentity->GetCookie(&uidNewUserID);

                        hr = _SwitchToUser(&rUser.uidUserID, &uidNewUserID);
                        *ppIdentity = pIdentity;
                    }

                    if (FAILED(hr))
                    {
                        UINT    iMsgId = idsSwitchCancelled;

                        pIdentity->Release();
                        *ppIdentity = NULL;

                        SetForegroundWindow(hwndParent);
                    
                        // could switch on some error codes to set iMsgId to 
                        // other error messages.  For now, skip showing the
                        // message if a user did the cancelling
                        if (hr != E_USER_CANCELLED)
                            MU_ShowErrorMessage(hwndParent, iMsgId, idsSwitchCancelCaption);
                    }
                }
            }
        }
        else
            hr = E_USER_CANCELLED;
    }

    if (!g_fReleasedMutex)
        ReleaseMutex(g_hMutex);

    return hr;
}


STDMETHODIMP CUserIdentityManager::Logoff(HWND hwndParent)
{
    GUID        uidToID = GUID_NULL;
    HRESULT     hr;
    USERINFO    rUser;
    BOOL        fGotUser;

    if (!g_hMutex)
        return E_UNEXPECTED;

    DWORD dwWaitResult;
    dwWaitResult = WaitForSingleObject(g_hMutex, INFINITE);  
    
    if (dwWaitResult != WAIT_OBJECT_0)
        return E_UNEXPECTED;

    fGotUser = MU_GetUserInfo(NULL, &rUser);
    if (!fGotUser)
        rUser.uidUserID = GUID_NULL;

    // switch to the null user
    hr = _SwitchToUser(&rUser.uidUserID, &uidToID);

    if (FAILED(hr))
    {
        UINT    iMsgId = idsLogoutCancelled;

        SetForegroundWindow(hwndParent);
        
        // could switch on some error codes to set iMsgId to 
        // other error messages.  For now, skip showing the
        // message if a user did the cancelling
        if (hr != E_USER_CANCELLED)
            MU_ShowErrorMessage(hwndParent, iMsgId, idsSwitchCancelCaption);
    }

    ReleaseMutex(g_hMutex);

    return hr;
}

STDMETHODIMP CUserIdentityManager::_SwitchToUser(GUID *puidFromUser, GUID *puidToUser)
{
    TCHAR   szUsername[CCH_USERNAME_MAX_LENGTH+1] = "";
    HRESULT hr;

    // switching to the same user is automatically OK.
    if (*puidFromUser == *puidToUser)
        return S_OK;

    // Set up the from and to users
    g_uidOldUserId = *puidFromUser;
    g_uidNewUserId = *puidToUser;
    g_fNotifyComplete = FALSE;
    _PersistChangingIdentities();
    if (*puidToUser != GUID_NULL)
        MU_UserIdToUsername(puidToUser, szUsername, CCH_USERNAME_MAX_LENGTH);
        
    // Notify window's that a switch is coming
    if (SUCCEEDED(hr = _QueryProcessesCanSwitch()))
    {
        if (SUCCEEDED(hr = MU_SwitchToUser(szUsername)))
        {
            if (!g_fReleasedMutex)
            {
                g_fReleasedMutex = true;
                g_fNotifyComplete = true;
                ReleaseMutex(g_hMutex);
            }
            _NotifyIdentitiesSwitched();
        }
    }
    g_fNotifyComplete = TRUE;

    // clear these back out again
    g_uidOldUserId = GUID_NULL;
    g_uidNewUserId = GUID_NULL;
    ClearChangingIdentities();

    return hr;
}

STDMETHODIMP CUserIdentityManager::GetIdentityByCookie(GUID *uidCookie, IUserIdentity **ppIdentity)
{
    CUserIdentity *pIdentity;
    HRESULT hr = E_IDENTITY_NOT_FOUND;
    GUID        uidUserCookie = *uidCookie;

    *ppIdentity = NULL;

    if (MU_IdentitiesDisabled())
    {
        // if disabled, they can only get the default identity. 
        // if asking for the current, they will get the defalt.
        // if asking for default by the constant or the default's guid, then succeed.
        // otherwise return an error.
        if (!MU_GetDefaultUserID(&uidUserCookie))
            return E_IDENTITY_NOT_FOUND;
        
        if (UID_GIBC_CURRENT_USER == uidUserCookie)
            uidUserCookie = UID_GIBC_DEFAULT_USER;

        if (!(uidUserCookie == uidUserCookie || UID_GIBC_DEFAULT_USER == uidUserCookie))
            return E_IDENTITIES_DISABLED;
    }


    if (uidUserCookie  == UID_GIBC_DEFAULT_USER)
    {
        if (!MU_GetDefaultUserID(&uidUserCookie))
            return E_IDENTITY_NOT_FOUND;
    }
    else if (uidUserCookie  == UID_GIBC_CURRENT_USER)
    {
        if (!MU_GetCurrentUserID(&uidUserCookie))
            return E_NO_CURRENT_IDENTITY;
    }
    else if (uidUserCookie  == UID_GIBC_OUTGOING_USER)
    {
        _LoadChangingIdentities();
        if (g_uidOldUserId == GUID_NULL)
            return E_IDENTITY_NOT_FOUND;
        else
            uidUserCookie = g_uidOldUserId;
    }
    else if (uidUserCookie  == UID_GIBC_INCOMING_USER)
    {
        _LoadChangingIdentities();
        if (g_uidNewUserId == GUID_NULL)
            return E_IDENTITY_NOT_FOUND;
        else
            uidUserCookie = g_uidNewUserId;
    }

    pIdentity = new CUserIdentity;
    if (pIdentity)
    {
        hr = pIdentity->InitFromCookie(&uidUserCookie);

        if (SUCCEEDED(hr))
            *ppIdentity = pIdentity;
        else
        {
            // Cleanup
            delete pIdentity;
        }
    }

    return hr;
}

STDMETHODIMP CUserIdentityManager::GetConnectionInterface(IID *pIID)
{
    return E_NOTIMPL;
}

STDMETHODIMP CUserIdentityManager::GetConnectionPointContainer(IConnectionPointContainer **ppCPC)
{
    *ppCPC = NULL;
    return E_NOTIMPL;
}

STDMETHODIMP CUserIdentityManager::Advise(IUnknown *pUnkSink, DWORD *pdwCookie)
{
    HRESULT hr;
    EnterCriticalSection(&m_rCritSect);

    AddRef();

    if (!m_pAdviseRegistry)
        m_pAdviseRegistry = new CNotifierList;
    Assert(m_pAdviseRegistry);

    if (m_pAdviseRegistry)
    {
        if (!m_fWndRegistered)
            _CreateWindowClass();

        hr = m_pAdviseRegistry->Add(pUnkSink, pdwCookie);
    }
    else
        hr = E_OUTOFMEMORY;

    LeaveCriticalSection(&m_rCritSect);    
    return hr;
}

STDMETHODIMP CUserIdentityManager::Unadvise(DWORD dwCookie)
{
    HRESULT hr;

    EnterCriticalSection(&m_rCritSect);
    if (m_pAdviseRegistry)
    {
        hr = m_pAdviseRegistry->RemoveCookie(dwCookie);
    }
    else
        hr = E_FAIL;

    LeaveCriticalSection(&m_rCritSect);    

    Release();
    
    return hr;
}
        
STDMETHODIMP CUserIdentityManager::EnumConnections(IEnumConnections **ppEnum)
{
    *ppEnum = NULL;
    return E_NOTIMPL;
}

STDMETHODIMP CUserIdentityManager::QuerySwitchIdentities()
{
    HRESULT    hr = S_OK;
    DWORD    dwLength, dwIndex;

    if (!m_pAdviseRegistry)
        return S_OK;

    TraceCall("Identity - CUserIdentityManager::QuerySwitchIdentities");

    dwLength = m_pAdviseRegistry->GetLength();

    for (dwIndex = 0; dwIndex < dwLength; dwIndex++)
    {
        IUnknown    *punk;
        IIdentityChangeNotify    *pICNotify;
        if (SUCCEEDED(m_pAdviseRegistry->GetAtIndex(dwIndex, &punk)) && punk)
        {
            if (SUCCEEDED(punk->QueryInterface(IID_IIdentityChangeNotify, (void **)&pICNotify)) && pICNotify)
            {
                if (FAILED(hr = pICNotify->QuerySwitchIdentities()))
                {
                    punk->Release();
                    pICNotify->Release();
                    goto exit;
                }
                pICNotify->Release();
            }
            punk->Release();
        }
    }
exit:
    return hr;
}

STDMETHODIMP CUserIdentityManager::NotifySwitchIdentities()
{
    HRESULT    hr = S_OK;
    DWORD    dwLength, dwIndex;

    if (!m_pAdviseRegistry)
        return S_OK;

    TraceCall("Identity - CUserIdentityManager::NotifySwitchIdentities");

    dwLength = m_pAdviseRegistry->GetLength();

    for (dwIndex = 0; dwIndex < dwLength; dwIndex++)
    {
        IUnknown    *punk;
        IIdentityChangeNotify    *pICNotify;
        if (SUCCEEDED(m_pAdviseRegistry->GetAtIndex(dwIndex, &punk)) && punk)
        {
            if (SUCCEEDED(punk->QueryInterface(IID_IIdentityChangeNotify, (void **)&pICNotify)) && pICNotify)
            {
                if (FAILED(hr = pICNotify->SwitchIdentities()))
                {
                    punk->Release();
                    pICNotify->Release();
                    goto exit;
                }
                pICNotify->Release();
            }
            punk->Release();
        }
    }
exit:
    return hr;
}

STDMETHODIMP CUserIdentityManager::_QueryProcessesCanSwitch()
{
    HWND    hWnd, hNextWnd = NULL;
    LRESULT lResult;
    HWND   *prghwnd = NULL;
    DWORD   chwnd = 0, cAllocHwnd = 0, dw;
    HRESULT hr;

    TraceCall("Identity - CUserIdentityManager::_QueryProcessesCanSwitch");

    cAllocHwnd = 10;
    if (!MemAlloc((LPVOID*)(&prghwnd), cAllocHwnd * sizeof(HWND)))
        return E_OUTOFMEMORY;

    hWnd = GetTopWindow(NULL);
    while (hWnd)
    {
        hNextWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
        
        if (!IsWindowVisible(hWnd))
        {
            TCHAR   szWndClassName[255];

            GetClassName(hWnd,  szWndClassName, sizeof(szWndClassName));
            
            if (lstrcmp(szWndClassName, c_szNotifyWindowClass) == 0)
            {
                if (chwnd == cAllocHwnd)
                {
                    cAllocHwnd += 10;
                    if (!MemRealloc((LPVOID*)(&prghwnd), cAllocHwnd * sizeof(HWND)))
                    {
                        hr = E_OUTOFMEMORY;
                        goto exit;
                    }
                }
                prghwnd[chwnd++] = hWnd;
            }
        }

        hWnd = hNextWnd;
    }
    
    hr = S_OK;
    for (dw = 0; dw < chwnd; dw++)
    {
        if (IsWindow(prghwnd[dw]))
        {
            lResult = SendMessage(prghwnd[dw], WM_QUERY_IDENTITY_CHANGE, 0, 0);
            if (FAILED((HRESULT)lResult))
            {
                hr = (HRESULT)lResult;
                goto exit;
            }
        }
    }
exit:
    MemFree(prghwnd);
    prghwnd = NULL;
    return hr;
}

STDMETHODIMP CUserIdentityManager::_NotifyIdentitiesSwitched()
{
    HWND    hWnd, hNextWnd = NULL;
    LRESULT lResult;
    HWND   *prghwnd = NULL;
    DWORD   chwnd = 0, cAllocHwnd = 0, dw;

    TraceCall("Identity - CUserIdentityManager::_NotifyIdentitiesSwitched");

    cAllocHwnd = 10;
    if (!MemAlloc((LPVOID*)(&prghwnd), cAllocHwnd * sizeof(HWND)))
        return E_OUTOFMEMORY;

    hWnd = GetTopWindow(NULL);
    while (hWnd)
    {
        hNextWnd = GetNextWindow(hWnd, GW_HWNDNEXT);
        
        if (!IsWindowVisible(hWnd))
        {
            TCHAR   szWndClassName[255];

            GetClassName(hWnd,  szWndClassName, sizeof(szWndClassName));
            
            if (lstrcmp(szWndClassName, c_szNotifyWindowClass) == 0)
            {
                if (chwnd == cAllocHwnd)
                {
                    cAllocHwnd += 10;
                    if (!MemRealloc((LPVOID*)(&prghwnd), cAllocHwnd * sizeof(HWND)))
                        goto exit;
                }
                prghwnd[chwnd++] = hWnd;
            }
        }

        hWnd = hNextWnd;
    }
    
    for (dw = 0; dw < chwnd; dw++)
    {
        DWORD_PTR dwResult;
        if (IsWindow(prghwnd[dw]))
//            lResult = PostMessage(prghwnd[dw], WM_IDENTITY_CHANGED, 0, 0);    //Raid 48054
            SendMessageTimeout(prghwnd[dw], WM_IDENTITY_CHANGED, 0, 0, SMTO_ABORTIFHUNG | SMTO_NORMAL, 1500, &dwResult);
    }
exit:
    MemFree(prghwnd);
    prghwnd = NULL;
    return S_OK;
}

STDMETHODIMP CUserIdentityManager::_CreateWindowClass()
{
    WNDCLASS wc;    
        
    if (!m_fWndRegistered)            /*set up window class and register it */
    {
        wc.lpszClassName    = c_szNotifyWindowClass;
        wc.hInstance        = g_hInst;
        wc.lpfnWndProc      = CUserIdentityManager::WndProc;
        wc.hCursor          = NULL;
        wc.hIcon            = NULL;
        wc.lpszMenuName     = NULL;
        wc.hbrBackground    = NULL;
        wc.style            = CS_DBLCLKS;
        wc.cbClsExtra       = 0;
        wc.cbWndExtra       = 0;

        if (!RegisterClassA(&wc))
            return E_FAIL;

        m_fWndRegistered = TRUE;
    }

    return S_OK;
}


LRESULT CALLBACK CUserIdentityManager::WndProc(HWND hWnd, UINT messg, WPARAM wParam, LPARAM lParam)
{
    CNotifierList *pList = NULL;
    HRESULT  hr;

    if (messg == WM_QUERY_IDENTITY_CHANGE ||
        messg == WM_IDENTITY_CHANGED ||
        messg == WM_IDENTITY_INFO_CHANGED)
    {
#if defined(DEBUG)
        DebugStrf("Identity - CUserIdentityManager::WndProc() called for notification.\r\n");
#endif
        pList = (CNotifierList *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
        if (pList)
        {
            hr = pList->SendNotification(messg, (DWORD)lParam);
            return hr;
        }
    }
    else
    {
        switch(messg)
        {
            case WM_CREATE:
                LPCREATESTRUCT  pcs;

                pcs = (LPCREATESTRUCT)lParam;
                SetWindowLongPtr(hWnd, GWLP_USERDATA, (LRESULT)pcs->lpCreateParams);
                return(DefWindowProc(hWnd, messg, wParam, lParam));
                break;
/*
            case WM_QUERY_IDENTITY_CHANGE:
            case WM_IDENTITY_CHANGED:
            case WM_IDENTITY_INFO_CHANGED:
                DebugStrf("Identity - CUserIdentityManager::WndProc() called for notification.\r\n");
                pList = (CNotifierList *)GetWindowLongPtr(hWnd, GWLP_USERDATA);
                if (pList)
                {
                    hr = pList->SendNotification(messg, (DWORD)lParam);
                    return hr;
                }
                break;
*/
            case WM_CLOSE:
                SetWindowLongPtr(hWnd, GWLP_USERDATA, 0);
                return(DefWindowProc(hWnd, messg, wParam, lParam));
                break;

            default:
                return(DefWindowProc(hWnd, messg, wParam, lParam));
 
        }
    }
    return 0;
}

//----------------------------------------------------------------------------
//  Logon the specified user.
//  - Checks password
//----------------------------------------------------------------------------
STDMETHODIMP CUserIdentityManager::LogonAs(WCHAR *pszName, WCHAR *pszPassword, IUserIdentity **ppIdentity)
{
    CUserIdentity *pIdentity;
    HRESULT     hr = E_FAIL;
    USERINFO    rUser;
    GUID        uidNewUserID;
    BOOL        fGotUser;
    TCHAR       szName[CCH_USERNAME_MAX_LENGTH+1];

    if (WideCharToMultiByte(CP_ACP, 0, pszName, -1, szName, CCH_USERNAME_MAX_LENGTH, NULL, NULL) == 0)
    {
        return E_FAIL;
    }

    // if identities are disabled, always return the default identity.
    if (MU_IdentitiesDisabled())
    {
        hr = GetIdentityByCookie((GUID *)&UID_GIBC_DEFAULT_USER, ppIdentity);
        
        return (SUCCEEDED(hr) ? S_IDENTITIES_DISABLED : hr);
    }

    if (!g_hMutex)
        return E_UNEXPECTED;

    if (g_uidOldUserId != GUID_NULL || g_uidOldUserId != GUID_NULL)
    {
        // we are in the middle of a switch
        if (!g_fNotifyComplete)
        {
            return E_IDENTITY_CHANGING;
        }
    }

    *ppIdentity = NULL;

    //
    // Grab info on the current user
    //
    fGotUser = MU_GetUserInfo(NULL, &rUser);
    if (!fGotUser)
    {
        *rUser.szUsername = 0;
        ZeroMemory(&rUser.uidUserID, sizeof(GUID));
    }

    if (0 == *rUser.szUsername)
    {
        GUID uidStart;

        MU_GetLoginOption(&uidStart);
        if (GUID_NULL != uidStart)
        {
            MU_GetUserInfo(&uidStart, &rUser);
            rUser.uidUserID = GUID_NULL;
    
        }
    }

    pIdentity = new CUserIdentity;
    if (pIdentity)
    {
        hr = pIdentity->InitFromUsername(szName);
        if (SUCCEEDED(hr))
        {
            pIdentity->GetCookie(&uidNewUserID);
            hr= ConfirmPassword(&uidNewUserID, pszPassword);
            if (SUCCEEDED(hr))
            {
                hr = _SwitchToUser(&rUser.uidUserID, &uidNewUserID);

                if (SUCCEEDED(hr))
                {
                    *ppIdentity = pIdentity;
                }
                else
                {
                    UINT iMsgId = idsSwitchCancelled;

                    pIdentity->Release();
                    *ppIdentity = NULL;

                    // could switch on some error codes to set iMsgId to 
                    // other error messages.  For now, skip showing the
                    // message if a user did the cancelling
                    if (hr != E_USER_CANCELLED)
                        MU_ShowErrorMessage(NULL, iMsgId, idsSwitchCancelCaption);
                }
            } // ConfirmPassword()
        } // InitFromUsername()
    }

    if (!g_fReleasedMutex)
        ReleaseMutex(g_hMutex);

    return hr;
}

