//
// ident.cpp - implementation of CUserIdentity class
//
#include "private.h"
#include "shlwapi.h"
#include "multiusr.h"
#include "strconst.h"
#include "multiutl.h"
#include <shfolder.h>

//
// Constructor / destructor
//
CUserIdentity::CUserIdentity()
    : m_cRef(1),
      m_fSaved(FALSE),
      m_fUsePassword(0)
{
    m_szUsername[0] = 0;
    m_szPassword[0] = 0;
    ZeroMemory(&m_uidCookie, sizeof(GUID));

    DllAddRef();
}


CUserIdentity::~CUserIdentity()
{
    DllRelease();
}


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

    // Validate requested interface
    if(IID_IUnknown == riid)
    {
        *ppv = (IUnknown *)this;
    }
    else if ((IID_IUserIdentity == riid)
             || (IID_IUserIdentity2 == riid))
    {
        *ppv = (IUserIdentity2 *)this;
    }

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

    return E_NOINTERFACE;
}

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

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

    delete this;
    return 0L;
}


// 
// IUserIdentity members
//
STDMETHODIMP CUserIdentity::GetCookie(GUID *puidCookie)
{
    if (!m_fSaved)
        return E_INVALIDARG;

    *puidCookie = m_uidCookie;
    return S_OK;

}

STDMETHODIMP CUserIdentity::OpenIdentityRegKey(DWORD dwDesiredAccess, HKEY *phKey)
{
    TCHAR    szRootPath[MAX_PATH];
    HRESULT  hr = S_OK;

    if (!m_fSaved)
        return E_IDENTITY_NOT_FOUND;

    MU_GetRegRootForUserID(&m_uidCookie, szRootPath);

    hr = RegCreateKey(HKEY_CURRENT_USER, szRootPath, phKey);
    RegCloseKey(*phKey);

    hr = RegOpenKeyEx(HKEY_CURRENT_USER, szRootPath, 0, dwDesiredAccess, phKey);
    return (hr == ERROR_SUCCESS ? S_OK : E_FAIL);
}


STDMETHODIMP CUserIdentity::GetIdentityFolder(DWORD dwFlags, WCHAR *pszPath, ULONG ulBuffSize)
{
    WCHAR    szwRootPath[MAX_PATH];
    HRESULT hr;

    if (!m_fSaved)
        return E_IDENTITY_NOT_FOUND;

    hr = MU_GetUserDirectoryRoot(&m_uidCookie, dwFlags, szwRootPath, MAX_PATH);
    
    if (SUCCEEDED(hr))
    {
        StrCpyW(pszPath, szwRootPath);
    }

    return hr;
}



STDMETHODIMP CUserIdentity::GetName(WCHAR *pszName, ULONG ulBuffSize)
{
    if (!m_fSaved || ulBuffSize == 0)
        return E_IDENTITY_NOT_FOUND;

    if (MultiByteToWideChar(CP_ACP, 0, m_szUsername, -1, pszName, ulBuffSize) == 0)
        return GetLastError();
    
    return S_OK;
}

STDMETHODIMP CUserIdentity::SetName(WCHAR *pszName)
{
    TCHAR       szRegPath[MAX_PATH];
    HRESULT     hr = S_OK;
    HKEY        hKey;
    USERINFO    uiCurrent;
    LPARAM      lpNotify = IIC_CURRENT_IDENTITY_CHANGED;
    TCHAR       szUsername[CCH_USERNAME_MAX_LENGTH];
    
    if (WideCharToMultiByte(CP_ACP, 0, pszName, -1, szUsername, CCH_USERNAME_MAX_LENGTH, NULL, NULL) == 0)
    {
        return GetLastError();
    }

    //
    // Only perform change if the username doesn't already exist.
    //
    if (!MU_UsernameExists(szUsername) && strcmp(szUsername, m_szUsername) != 0)
    {
        strcpy( m_szUsername, szUsername );

        hr = _SaveUser();
    
        // if its not the current identity, then just broadcast that an identity changed
        if (MU_GetUserInfo(NULL, &uiCurrent) && (m_uidCookie != uiCurrent.uidUserID))
        {
            lpNotify = IIC_IDENTITY_CHANGED;
        }

        // tell apps that the user's name changed
        if (SUCCEEDED(hr))
        {
            PostMessage(HWND_BROADCAST, WM_IDENTITY_INFO_CHANGED, 0, lpNotify);
        }
    }
    else
    {
        hr = E_IDENTITY_EXISTS;
    }

    return hr;
}

STDMETHODIMP CUserIdentity::SetPassword(WCHAR *pszPassword)
{
#ifdef IDENTITY_PASSWORDS
    TCHAR       szRegPath[MAX_PATH];
    HRESULT     hr = S_OK;
    HKEY        hKey;
    
    if (!m_fSaved)
        return E_IDENTITY_NOT_FOUND;

    if (WideCharToMultiByte(CP_ACP, 0, pszPassword, -1, m_szPassword, CCH_USERPASSWORD_MAX_LENGTH, NULL, NULL) == 0)
        return GetLastError();
    
    m_fUsePassword = (*m_szPassword != 0);

    hr = _SaveUser();

    return hr;
#else
    return E_NOTIMPL;
#endif
}


STDMETHODIMP CUserIdentity::_SaveUser()
{
    DWORD   dwType, dwSize, dwValue, dwStatus;
    HKEY    hkCurrUser;
    TCHAR   szPath[MAX_PATH];
    TCHAR   szUid[255];
    HRESULT hr;

    if (*m_szUsername == 0)
        return E_INVALIDARG;

    if (!m_fUsePassword)
        m_szPassword[0] = 0;

    if (!m_fSaved)
        hr = _ClaimNextUserId(&m_uidCookie);

    Assert(m_uidCookie != GUID_NULL);
    Assert(SUCCEEDED(hr));

    //
    // Save our settings
    //
    USERINFO UserInfo;

    UserInfo.uidUserID= m_uidCookie;
    lstrcpy( UserInfo.szUsername, m_szUsername);
    UserInfo.fUsePassword= m_fUsePassword;
    UserInfo.fPasswordValid= m_fUsePassword;
    lstrcpy( UserInfo.szPassword, m_szPassword );
    
    BOOL bSuccess= MU_SetUserInfo(&UserInfo);

    if (bSuccess)
    {
        m_fSaved = TRUE;
        return S_OK;
    }
    else
    {
        return E_FAIL;
    }
}


STDMETHODIMP CUserIdentity::InitFromUsername(TCHAR *pszUsername)
{
    GUID   uidCookie;
    HRESULT hr;

    if(FAILED(hr = MU_UsernameToUserId(pszUsername, &uidCookie)))
        return hr;

    return InitFromCookie(&uidCookie);
}


STDMETHODIMP CUserIdentity::InitFromCookie(GUID *puidCookie)
{
    BOOL bSuccess;
    USERINFO UserInfo;
    HRESULT hrRet = E_FAIL;

    bSuccess = MU_GetUserInfo( puidCookie, &UserInfo );

    if (bSuccess)
    {
        m_fUsePassword = UserInfo.fUsePassword;
        lstrcpy( m_szUsername, UserInfo.szUsername );
        lstrcpy( m_szPassword, UserInfo.szPassword );
        m_uidCookie = UserInfo.uidUserID;
        m_fSaved = TRUE;
        hrRet = S_OK;
    }

    return hrRet;
}


STDMETHODIMP CUserIdentity::GetOrdinal(DWORD* pdwOrdinal)
{
    if (!pdwOrdinal)
    {
        return E_INVALIDARG;
    }

    HKEY    hSourceSubKey, hkUserKey;
    DWORD   dwSize, dwType;
    DWORD   dwIdentityOrdinal = 1, dwOrdinal = 0;
    TCHAR   szUid[MAX_PATH];
    HRESULT hr = E_FAIL;
    
    AStringFromGUID(&m_uidCookie,  szUid, MAX_PATH);
        
    if (RegCreateKey(HKEY_CURRENT_USER, c_szRegRoot, &hSourceSubKey) == ERROR_SUCCESS)
    {
        dwSize = sizeof(dwIdentityOrdinal);
        RegQueryValueEx(hSourceSubKey, c_szIdentityOrdinal, NULL, &dwType, (LPBYTE)&dwIdentityOrdinal, &dwSize);

        if (RegOpenKey(hSourceSubKey, szUid, &hkUserKey) == ERROR_SUCCESS)
        {
            if (RegQueryValueEx(hkUserKey, c_szIdentityOrdinal, NULL, &dwType, (LPBYTE)&dwOrdinal, &dwSize)!=ERROR_SUCCESS)
            {
                if (RegSetValueEx(hkUserKey, c_szIdentityOrdinal, NULL, REG_DWORD, (LPBYTE)&dwIdentityOrdinal, dwSize)==ERROR_SUCCESS)
                {
                    dwOrdinal = dwIdentityOrdinal++;
                    RegSetValueEx(hSourceSubKey, c_szIdentityOrdinal, 0, REG_DWORD, (LPBYTE)&dwIdentityOrdinal, dwSize);
                    hr = S_OK;
                }
                else
                {
                    AssertSz(FALSE, "Couldn't set the identity ordinal");
                }
            }
            else
            {
                hr = S_OK;
            }
            
            RegCloseKey(hkUserKey); 
        }
        else
        {
            AssertSz(FALSE, "Couldn't open user's Key");
        }
        
        RegCloseKey(hSourceSubKey);
    }
    else
    {
        AssertSz(FALSE, "Couldn't open user profiles root Key");
    }

    *pdwOrdinal = dwOrdinal;
    return hr;
}

//----------------------------------------------------------------------------
//  Changes password to newPass if oldPass matches the current password
//----------------------------------------------------------------------------
STDMETHODIMP CUserIdentity::ChangePassword(WCHAR *szOldPass, WCHAR *szNewPass)
{
    HRESULT     hr = E_FAIL;
    TCHAR       szOldPwd[CCH_USERPASSWORD_MAX_LENGTH+1];
    
    if (!m_fSaved)
    {
        return E_IDENTITY_NOT_FOUND;
    }

    if (WideCharToMultiByte(CP_ACP, 0, szOldPass, -1, szOldPwd, CCH_USERPASSWORD_MAX_LENGTH, NULL, NULL) == 0)
    {
        return E_FAIL;
    }

    if (!m_fUsePassword || lstrcmp(szOldPwd, m_szPassword) == 0)
    {
        hr = SetPassword(szNewPass);
    }
    return hr;
}