#include "private.h"
#include "multiutl.h"
#include <wtypes.h>
#include "strconst.h"
#include <platform.h>

extern      HINSTANCE   g_hInst;

// Pstore related variables.
static PST_KEY s_Key = PST_KEY_CURRENT_USER;

// {89C39569-6841-11d2-9F59-0000F8085266}
static const GUID GUID_PStoreType = { 0x89c39569, 0x6841, 0x11d2, { 0x9f, 0x59, 0x0, 0x0, 0xf8, 0x8, 0x52, 0x66 } };
static WCHAR c_szIdentityMgr[] = L"IdentityMgr";
static WCHAR c_szIdentities[] = L"Identities";
static WCHAR c_szIdentityPass[] = L"IdentitiesPass";

//Need these private implementations
//OE has dependency on the particular allocator used

void *  __cdecl operator new(size_t nSize)
{
    // Zero init just to save some headaches
    return CoTaskMemAlloc(nSize);
}

void  __cdecl operator delete(void *pv)
{
    //If changed to GlobalFree or HeapFree - must check for NULL here
    CoTaskMemFree(pv);
}

extern "C" int __cdecl _purecall(void) 
{
    DebugBreak();
    return 0;
}


// --------------------------------------------------------------------------
// FIsSpaceA
// --------------------------------------------------------------------------
BOOL FIsSpaceA(LPSTR psz)
{
#ifdef MAC
    return (isspace(*psz));
#else	// !MAC
    WORD wType;

    if (IsDBCSLeadByte(*psz))
        GetStringTypeExA(LOCALE_USER_DEFAULT, CT_CTYPE1, psz, 2, &wType);
    else
        GetStringTypeExA(LOCALE_USER_DEFAULT, CT_CTYPE1, psz, 1, &wType);
    return (wType & C1_SPACE);
#endif	// MAC
}

// --------------------------------------------------------------------------
// FIsSpaceW
// --------------------------------------------------------------------------
BOOL FIsSpaceW(LPWSTR psz)
{
#ifdef MAC
    // Maybe we should convert to ANSI before checking??
    return (isspace(*( ( (TCHAR *) psz ) + 1 ) ));
#else	// !MAC
    WORD wType;
    GetStringTypeExW(LOCALE_USER_DEFAULT, CT_CTYPE1, psz, 1, &wType);
    return (wType & C1_SPACE);
#endif	// !MAC
}


ULONG UlStripWhitespace(LPTSTR lpsz, BOOL fLeading, BOOL fTrailing, ULONG *pcb)
{
    // Locals
    ULONG           cb;
    LPTSTR          psz;
    
    Assert(lpsz != NULL);
    Assert(fLeading || fTrailing);
    
    // Did the user pass in the length
    if (pcb)
        cb = *pcb;
    else
        cb = lstrlen (lpsz);
    
    if (cb == 0)
        return cb;
    
    if (fLeading)
    {
        psz = lpsz;
        
        while (FIsSpace(psz))
        {
            psz++;
            cb--;
        }
        
        if (psz != lpsz)
            // get the NULL at the end too
            MoveMemory(lpsz, psz, (cb + 1) * sizeof(TCHAR));
    }
    
    if (fTrailing)
    {
        psz = lpsz + cb;
        
        while (cb > 0)
        {
            if (!FIsSpace(psz-1))
                break;
            psz--;
            cb--;
        }    
        
        // NULL Term
        *psz = '\0';
    }
    
    // Set String Size
    if (pcb)
        *pcb = cb;
    
    // Done
    return cb;
}

BOOL OnContextHelp(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, HELPMAP const * rgCtxMap)
{
    if (uMsg == WM_HELP)
    {
        LPHELPINFO lphi = (LPHELPINFO) lParam;
        if (lphi->iContextType == HELPINFO_WINDOW)   // must be for a control
        {
            WinHelp ((HWND)lphi->hItemHandle,
                        c_szCtxHelpFile,
                        HELP_WM_HELP,
                        (DWORD_PTR)(void*)rgCtxMap);
        }
        return (TRUE);
    }
    else if (uMsg == WM_CONTEXTMENU)
    {
        WinHelp ((HWND) wParam,
                    c_szCtxHelpFile,
                    HELP_CONTEXTMENU,
                    (DWORD_PTR)(void*)rgCtxMap);
        return (TRUE);
    }

    Assert(0);

    return FALSE;
}



#define OBFUSCATOR              0x14151875;

#define PROT_SIZEOF_HEADER      0x02    // 2 bytes in the header
#define PROT_SIZEOF_XORHEADER   (PROT_SIZEOF_HEADER+sizeof(DWORD))

#define PROT_VERSION_1          0x01

#define PROT_PASS_XOR           0x01
#define PROT_PASS_PST           0x02

static BOOL FDataIsValidV1(BYTE *pb)
{ return pb && pb[0] == PROT_VERSION_1 && (pb[1] == PROT_PASS_XOR || pb[1] == PROT_PASS_PST); }

static BOOL FDataIsPST(BYTE *pb)
{ return pb && pb[1] == PROT_PASS_PST; }

///////////////////////////////////////////////////////////////////////////
// 
// NOTE - The functions for encoding the user passwords really should not 
//        be here.  Unfortunately, they are not anywhere else so for now,
//        this is where they will stay.  They are defined as static since
//        other code should not rely on them staying here, particularly the 
//        XOR stuff.
//
///////////////////////////////////////////////////////////////////////////
// 
// XOR functions
//
///////////////////////////////////////////////////////////////////////////

static HRESULT _XOREncodeProp(const BLOB *const pClear, BLOB *const pEncoded)
{
    DWORD       dwSize;
    DWORD       last, last2;
    DWORD       UNALIGNED *pdwCypher;
    DWORD       dex;

    pEncoded->cbSize = pClear->cbSize+PROT_SIZEOF_XORHEADER;
    if (!MemAlloc((void* *)&pEncoded->pBlobData, pEncoded->cbSize + 6))
        return E_OUTOFMEMORY;
    
    // set up header data
    Assert(2 == PROT_SIZEOF_HEADER);
    pEncoded->pBlobData[0] = PROT_VERSION_1;
    pEncoded->pBlobData[1] = PROT_PASS_XOR;
    *((DWORD UNALIGNED *)&(pEncoded->pBlobData[2])) = pClear->cbSize;

    // nevermind that the pointer is offset by the header size, this is
    // where we start to write out the modified password
    pdwCypher = (DWORD UNALIGNED *)&(pEncoded->pBlobData[PROT_SIZEOF_XORHEADER]);

    dex = 0;
    last = OBFUSCATOR;                              // 0' = 0 ^ ob
    if (dwSize = pClear->cbSize / sizeof(DWORD))
        {
        // case where data is >= 4 bytes
        for (; dex < dwSize; dex++)
            {
            last2 = ((DWORD UNALIGNED *)pClear->pBlobData)[dex];  // 1 
            pdwCypher[dex] = last2 ^ last;              // 1' = 1 ^ 0
            last = last2;                   // save 1 for the 2 round
            }
        }

    // if we have bits left over
    // note that dwSize is computed now in bits
    if (dwSize = (pClear->cbSize % sizeof(DWORD))*8)
        {
        // need to not munge memory that isn't ours
        last >>= sizeof(DWORD)*8-dwSize;
        pdwCypher[dex] &= ((DWORD)-1) << dwSize;
        pdwCypher[dex] |=
            ((((DWORD UNALIGNED *)pClear->pBlobData)[dex] & (((DWORD)-1) >> (sizeof(DWORD)*8-dwSize))) ^ last);
        }

    return S_OK;
}

static HRESULT _XORDecodeProp(const BLOB *const pEncoded, BLOB *const pClear)
{
    DWORD       dwSize;
    DWORD       last;
    DWORD       UNALIGNED *pdwCypher;
    DWORD       dex;

    // we use CoTaskMemAlloc to be in line with the PST implementation
    pClear->cbSize = *(DWORD UNALIGNED *)(&pEncoded->pBlobData[2]);
    MemAlloc((void **)&pClear->pBlobData, pClear->cbSize);
    if (!pClear->pBlobData)
        return E_OUTOFMEMORY;
    
    // should have been tested by now
    Assert(FDataIsValidV1(pEncoded->pBlobData));
    Assert(!FDataIsPST(pEncoded->pBlobData));

    // nevermind that the pointer is offset by the header size, this is
    // where the password starts
    pdwCypher = (DWORD UNALIGNED *)&(pEncoded->pBlobData[PROT_SIZEOF_XORHEADER]);

    dex = 0;
    last = OBFUSCATOR;
    if (dwSize = pClear->cbSize / sizeof(DWORD))
        {
        // case where data is >= 4 bytes
        for (; dex < dwSize; dex++)
            last = ((DWORD UNALIGNED *)pClear->pBlobData)[dex] = pdwCypher[dex] ^ last;
        }

    // if we have bits left over
    if (dwSize = (pClear->cbSize % sizeof(DWORD))*8)
        {
        // need to not munge memory that isn't ours
        last >>= sizeof(DWORD)*8-dwSize;
        ((DWORD UNALIGNED *)pClear->pBlobData)[dex] &= ((DWORD)-1) << dwSize;
        ((DWORD UNALIGNED *)pClear->pBlobData)[dex] |=
                ((pdwCypher[dex] & (((DWORD)-1) >> (sizeof(DWORD)*8-dwSize))) ^ last);
        }

    return S_OK;
}

/*
    EncodeUserPassword

    Encrypt the passed in password.  This encryption seems to
    add an extra 6 bytes on to the beginning of the data
    that it passes back, so we need to make sure that the 
    lpszPwd is large enough to hold a few extra characters.
    *cb should be different on return than it was when it 
    was passed in.

    Parameters:
    lpszPwd - on entry, a c string containing the password.
    on exit, it is the encrypted data, plus some header info.

    cb - the size of lpszPwd on entry and exit.  Note that it should
    include the trailing null, so "foo" would enter with *cb == 4.
*/
void EncodeUserPassword(TCHAR *lpszPwd, ULONG *cb)
{
    BLOB            blobClient;
    BLOB            blobProp;

    blobClient.pBlobData= (BYTE *)lpszPwd;
    blobClient.cbSize   = *cb;
    blobProp.pBlobData  = NULL;
    blobProp.cbSize     = 0;
    
    _XOREncodeProp(&blobClient, &blobProp);
    
    if (blobProp.pBlobData)
    {
        memcpy(lpszPwd, blobProp.pBlobData, blobProp.cbSize);
        *cb = blobProp.cbSize;
        MemFree(blobProp.pBlobData);
    }
}

/*
    DecodeUserPassword

    Decrypt the passed in data and return a password.  This 
    encryption seems to add an extra 6 bytes on to the beginning 
    so decrupting will result in a using less of lpszPwd.
    .
    *cb should be different on return than it was when it 
    was passed in.

    Parameters:
    lpszPwd - on entry, the encrypted password plus some 
    header info. 
    on exit, a c string containing the password.

    cb - the size of lpszPwd on entry and exit.  Note that it should
    include the trailing null, so "foo" would leave with *cb == 4.
*/
void DecodeUserPassword(TCHAR *lpszPwd, ULONG *cb)
{
    BLOB            blobClient;
    BLOB            blobProp;

    blobClient.pBlobData= (BYTE *)lpszPwd;
    blobClient.cbSize   = *cb;
    blobProp.pBlobData  = NULL;
    blobProp.cbSize     = 0;
    
    _XORDecodeProp(&blobClient, &blobProp);

    if (blobProp.pBlobData)
    {
        memcpy(lpszPwd, blobProp.pBlobData, blobProp.cbSize);
        lpszPwd[blobProp.cbSize] = 0;
        *cb = blobProp.cbSize;
        MemFree(blobProp.pBlobData);
    }
}


// --------------------------------------------------------------------------------
// MemInit
// --------------------------------------------------------------------------------
void MemInit()
{
}

// --------------------------------------------------------------------------------
// MemUnInit
// --------------------------------------------------------------------------------
void MemUnInit()
{
}


// --------------------------------------------------------------------------------
// MemFree
// --------------------------------------------------------------------------------
void MemFree(void* pv) 
{
    CoTaskMemFree(pv);
}

// --------------------------------------------------------------------------------
// MemAlloc
// --------------------------------------------------------------------------------
BOOL MemAlloc(void** ppv, ULONG cb) 
{
    assert(ppv && cb);
    *ppv = CoTaskMemAlloc(cb);
    if (NULL == *ppv)
        return FALSE;
    return TRUE;
}

// --------------------------------------------------------------------------------
// MemRealloc
// --------------------------------------------------------------------------------
BOOL MemRealloc(void* *ppv, ULONG cbNew) 
{
    assert(ppv && cbNew);
    void* pv = CoTaskMemRealloc(*ppv, cbNew);
    if (NULL == pv)
        return FALSE;
    *ppv = pv;
    return TRUE;
}

// --------------------------------------------------------------------------------
//  Functions to convert GUIDs to ascii strings
// --------------------------------------------------------------------------------

int AStringFromGUID(GUID *puid,  TCHAR *lpsz, int cch)
{
    WCHAR   wsz[255];
    int     i;

    i = StringFromGUID2(*puid, wsz, 255);

    if (WideCharToMultiByte(CP_ACP, 0, wsz, -1, lpsz, cch, NULL, NULL) == 0)
        return 0;
    
    return (lstrlen(lpsz) + 1);
}


HRESULT GUIDFromAString(TCHAR *lpsz, GUID *puid)
{
    WCHAR   wsz[255];
    HRESULT hr;

    if (MultiByteToWideChar(CP_ACP, 0, lpsz, -1, wsz, 255) == 0)
        return GetLastError();

    hr = CLSIDFromString(wsz, puid);
    
    return hr;
}



// ****************************************************************************************************
//  CNotifierList Class
//
//  A really basic IUnknown list class.  Actually, its a IUnknown array class, but you don't need to know
//  that.
//


CNotifierList::CNotifierList()
{
    m_count = 0;
    m_ptrCount = 0;
    m_entries = NULL;
    m_nextCookie = 1;
    m_cRef = 1;
    InitializeCriticalSection(&m_rCritSect);
}

/*
    CNotifierList::~CNotifierList

    Clean up any memory that was allocated in the CNotifierList object
*/
CNotifierList::~CNotifierList()
{
    if (m_entries)
    {
        for (int i = 0; i < m_count; i++)
        {
            if (m_entries[i].punk)
            {
                m_entries[i].punk->Release();
                m_entries[i].punk = NULL;
                m_entries[i].dwCookie = 0;
            }
        }
        MemFree(m_entries);
        m_entries = NULL;
        m_count = 0;
    }
    DeleteCriticalSection(&m_rCritSect);
}


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

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

    delete this;
    return 0L;
}

/*
    CNotifierList::Add

    Add a IUnknown to the end of the IUnknown list.
*/
HRESULT    CNotifierList::Add(IUnknown *punk, DWORD *pdwCookie)
{
    TraceCall("Identity - CNotifierList::Add");

    EnterCriticalSection(&m_rCritSect);
    // make more room for pointers, if necessary
    if (m_ptrCount == m_count)
    {
        m_ptrCount += 5;
        if (!MemRealloc((void **)&m_entries, sizeof(UNKLIST_ENTRY) * m_ptrCount))
        {
            m_ptrCount -= 5;
            Assert(false);
            LeaveCriticalSection(&m_rCritSect);    
            return E_OUTOFMEMORY;
        }

        // initialize the new IUnknowns to nil
        for (int i = m_count; i < m_ptrCount; i++)
        {
            ZeroMemory(&m_entries[i], sizeof(UNKLIST_ENTRY));
        }
    }
    
    //now put the IUnknown in the next location
    int iNewIndex = m_count++;
    
    punk->AddRef();
    m_entries[iNewIndex].punk = punk;
    m_entries[iNewIndex].bState = NS_NONE;
    m_entries[iNewIndex].dwCookie = ++m_nextCookie;
    m_entries[iNewIndex].dwThreadId = GetCurrentThreadId();
    *pdwCookie = m_entries[iNewIndex].dwCookie;
    LeaveCriticalSection(&m_rCritSect);  
    CreateNotifyWindow();
    return S_OK;
}

/*
    CNotifierList::Remove
    
    Remove a IUnknown at zero based index iIndex 
*/

HRESULT CNotifierList::Remove(int   iIndex)
{
    int     iCopySize;

    TraceCall("Identity - CNotifierList::Remove");

    EnterCriticalSection(&m_rCritSect);
    iCopySize = ((m_count - iIndex) - 1) * sizeof(UNKLIST_ENTRY);

    // free the memory for the IUnknown
    if (m_entries[iIndex].punk)
    {
        ReleaseWindow();
        m_entries[iIndex].punk->Release();
        ZeroMemory(&m_entries[iIndex], sizeof(UNKLIST_ENTRY));
    }

    // move the other IUnknowns down
    if (iCopySize)
    {
        memmove(&(m_entries[iIndex]), &(m_entries[iIndex+1]), iCopySize);
    }

    // null out the last item in the list and decrement the counter.
    m_entries[--m_count].punk = NULL;
    LeaveCriticalSection(&m_rCritSect); 
    return S_OK;
}

/*
    CNotifierList::RemoveCookie
    
    Remove an IUnknown by its cookie
*/

HRESULT    CNotifierList::RemoveCookie(DWORD dwCookie)
{
    int     iIndex;

    for (iIndex = 0; iIndex < m_count; iIndex++)
    {
        if (m_entries[iIndex].dwCookie == dwCookie)
        {
            return Remove(iIndex);
        }
    }
    return E_FAIL;
}

/*
    CNotifierList::GetAtIndex
    
    Return the pointer to the IUnknown at zero based index iIndex.

    Return the IUnknown at the given index.  Note that the object pointer
    is still owned by the IUnknown list and should not be deleted.
*/

HRESULT     CNotifierList::GetAtIndex(int iIndex, IUnknown **ppunk)
{
    HRESULT hr = E_FAIL;

    EnterCriticalSection(&m_rCritSect);
    if (iIndex < m_count && iIndex >= 0 && m_entries[iIndex].punk)
    {
        *ppunk = m_entries[iIndex].punk;
        (*ppunk)->AddRef();
        hr = S_OK;
    }
    else
        *ppunk = NULL;

    LeaveCriticalSection(&m_rCritSect);    
    return hr;
}


HRESULT     CNotifierList::CreateNotifyWindow()
{
    DWORD   dwThreadCount = 0;
    DWORD   dwThreadId = GetCurrentThreadId();
    int     iIndex;
    int     iFound = -1;
    HWND    hwnd = NULL;

    for (iIndex = 0; iIndex < m_count; iIndex++)
    {
        if (m_entries[iIndex].dwThreadId == dwThreadId)
        {
            iFound  = iIndex;
            if (!hwnd)
                hwnd = m_entries[iIndex].hwnd;
            else
            {
                Assert(NULL == m_entries[iIndex].hwnd || hwnd == m_entries[iIndex].hwnd);
                m_entries[iIndex].hwnd = hwnd;
            }
            dwThreadCount++;
        }
    }
    
    if (dwThreadCount == 1 && iFound >= 0)
    {
        hwnd = m_entries[iFound].hwnd = CreateWindowA(c_szNotifyWindowClass, c_szNotifyWindowClass, WS_POPUP, 
                    CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, 
                    NULL, g_hInst, this);
        
        if (m_entries[iFound].hwnd)
            SetWindowLongPtr(m_entries[iFound].hwnd, GWLP_USERDATA, (LRESULT)this);
    }
    return (hwnd ? S_OK : E_FAIL);
}


HRESULT     CNotifierList::ReleaseWindow()
{
    DWORD   dwThreadCount = 0;
    DWORD   dwThreadId = GetCurrentThreadId();
    int     iIndex;
    HWND    hwnd = NULL;

    for (iIndex = 0; iIndex < m_count; iIndex++)
    {
        if (m_entries[iIndex].dwThreadId == dwThreadId)
        {
            if (dwThreadCount == 0)
                hwnd = m_entries[iIndex].hwnd;
            dwThreadCount++;
        }
    }
    
    if (dwThreadCount == 1 && hwnd)
    {
        SendMessage(hwnd, WM_CLOSE, 0, 0);
    }
    return S_OK;
}

HRESULT     CNotifierList::PreNotify()
{
    DWORD   dwThreadId = GetCurrentThreadId();
    int     iIndex;

    for (iIndex = m_count -1; iIndex >= 0; iIndex--)
    {
        if (m_entries[iIndex].dwThreadId == dwThreadId && NULL != m_entries[iIndex].punk)
            m_entries[iIndex].bState = NS_PRE_NOTIFY;
//        else          //BUG 47472, this could cause problems during re-entrant calls to SendNotification
//            m_entries[iIndex].bState = NS_NONE;
    }
    return S_OK;
}

int     CNotifierList::GetNextNotify()
{
    DWORD   dwThreadId = GetCurrentThreadId();
    int     iIndex;
    for (iIndex = m_count -1; iIndex >= 0; iIndex--)
    {
        if (m_entries[iIndex].dwThreadId == dwThreadId && NULL != m_entries[iIndex].punk && NS_PRE_NOTIFY == m_entries[iIndex].bState)
            return iIndex;
    }
    return -1;
}

HRESULT     CNotifierList::SendNotification(UINT msg, DWORD dwType)
{
    DWORD   dwThreadCount = 0, dwOldCount;
    DWORD   dwThreadId = GetCurrentThreadId();
    int     iIndex;
    HWND    hwnd = NULL;
    HRESULT hr = S_OK;

#if defined(DEBUG)
    DebugStrf("Identity - CNotifierList::SendNotification %ld\r\n", msg);
#endif

    AddRef();

    PreNotify();

    while ((iIndex = GetNextNotify()) != -1)
    {
        IUnknown    *punk;
        IIdentityChangeNotify    *pICNotify;

        punk = m_entries[iIndex].punk;
        m_entries[iIndex].bState = NS_NOTIFIED;

        punk->AddRef();
        if (SUCCEEDED(punk->QueryInterface(IID_IIdentityChangeNotify, (void **)&pICNotify)) && pICNotify)
        {
            if( msg == WM_QUERY_IDENTITY_CHANGE )
            {
                if (FAILED(hr = pICNotify->QuerySwitchIdentities()))
                {
                    punk->Release();
                    pICNotify->Release();
                    goto exit;
                }
            }
            else if( msg == WM_IDENTITY_CHANGED )
            {
                pICNotify->SwitchIdentities();
            }
            else if( msg == WM_IDENTITY_INFO_CHANGED )
            {
                    pICNotify->IdentityInformationChanged(dwType);
            }

            pICNotify->Release();
        }
        punk->Release();
    }

exit:
    Release();
    return hr;
}

#ifdef DEBUG

// --------------------------------------------------------------------------------
// DebugStrf
// --------------------------------------------------------------------------------
void DebugStrf(LPTSTR lpszFormat, ...)
{
    static TCHAR szDebugBuff[500];
    va_list arglist;

    va_start(arglist, lpszFormat);
    wvsprintf(szDebugBuff, lpszFormat, arglist);
    va_end(arglist);

    OutputDebugString(szDebugBuff);
}
#endif



// ---------------------------------------------------------------------------------
// Pstore code for storing passwords
// ---------------------------------------------------------------------------------
// Functions related to saving and restoring user passwords from the pstore.


// We have wrappers around Create and Release to allow for future caching of the pstore
// instance within webcheck. 

STDAPI CreatePStore(IPStore **ppIPStore)
{
    HRESULT hr;

    hr = PStoreCreateInstance ( ppIPStore,
                                NULL,
                                NULL,
                                0);
    return hr;
}


STDAPI ReleasePStore(IPStore *pIPStore)
{
    HRESULT hr;

    if (pIPStore)
    {
        pIPStore->Release();
        hr = S_OK;
    }
    else
    {
        hr = E_POINTER;
    }

    return hr;
}


STDAPI  ReadIdentityPassword(GUID *puidIdentity, PASSWORD_STORE  *pPwdStore)
{
    GUID             itemType = GUID_NULL;
    GUID             itemSubtype = GUID_NULL;
    PST_PROMPTINFO   promptInfo = {0};
    IPStore*         pStore = NULL;
    HRESULT          hr ;
     
    if (pPwdStore == NULL)
        return E_POINTER;

    promptInfo.cbSize = sizeof(promptInfo);
    promptInfo.szPrompt = NULL;
    promptInfo.dwPromptFlags = 0;
    promptInfo.hwndApp = NULL;
    
    hr = CreatePStore(&pStore);    

    if (SUCCEEDED(hr))
    {
        Assert(pStore != NULL);

        itemType = GUID_PStoreType;
        itemSubtype = *puidIdentity;

        if (SUCCEEDED(hr))
        {
            DWORD           cbData;
            BYTE           *pbData = NULL;
            

            hr = pStore->ReadItem(
                            s_Key,
                            &itemType,
                            &itemSubtype,
                            c_szIdentityPass,
                            &cbData,
                            &pbData,
                            &promptInfo,
                            0);

            if (SUCCEEDED(hr))
            {
                CopyMemory(pPwdStore, pbData, (cbData <= sizeof(PASSWORD_STORE) ? cbData : sizeof(PASSWORD_STORE)));
                MemFree(pbData);

                hr = S_OK;
            }
        }

        ReleasePStore(pStore);
    }

    return hr;
}

STDAPI WriteIdentityPassword(GUID *puidIdentity, PASSWORD_STORE  *pPwdStore)
{
    HRESULT         hr;
    PST_TYPEINFO    typeInfo;
    PST_PROMPTINFO  promptInfo;
    IPStore *       pStore;

    typeInfo.cbSize = sizeof(typeInfo);

    typeInfo.szDisplayName = c_szIdentityMgr;

    promptInfo.cbSize = sizeof(promptInfo);
    promptInfo.dwPromptFlags = 0;
    promptInfo.hwndApp = NULL;
    promptInfo.szPrompt = NULL;

    hr = CreatePStore(&pStore);

    if (SUCCEEDED(hr))
    {
        GUID itemType = GUID_NULL;
        GUID itemSubtype = GUID_NULL;

        Assert(pStore != NULL);

        itemType = GUID_PStoreType;
        itemSubtype = *puidIdentity;
        
        if (SUCCEEDED(hr))
        {
            hr = pStore->CreateType(s_Key, &itemType, &typeInfo, 0);

            // PST_E_TYPE_EXISTS implies type already exists which is just fine
            // by us.
            if (SUCCEEDED(hr) || hr == PST_E_TYPE_EXISTS)
            {
                typeInfo.szDisplayName = c_szIdentities;

                hr = pStore->CreateSubtype(
                                        s_Key,
                                        &itemType,
                                        &itemSubtype,
                                        &typeInfo,
                                        NULL,
                                        0);

                if (SUCCEEDED(hr) || hr == PST_E_TYPE_EXISTS)
                {
                    if (pPwdStore != NULL)
                    {
                        hr = pStore->WriteItem(
                                            s_Key,
                                            &itemType,
                                            &itemSubtype,
                                            c_szIdentityPass,
                                            (sizeof(PASSWORD_STORE)),
                                            (BYTE *)pPwdStore,
                                            &promptInfo,
                                            PST_CF_NONE,
                                            0);
                    }
                    else
                    {
                        hr = pStore->DeleteItem(
                                            s_Key,
                                            &itemType,
                                            &itemSubtype,
                                            c_szIdentityPass,
                                            &promptInfo,
                                            0);
                    }
                }
            }
        }
        
        ReleasePStore(pStore);
    }
    
    return hr;
}              

#define CH_WHACK TEXT(FILENAME_SEPARATOR)
                                                         
// rips the last part of the path off including the backslash
//      C:\foo      -> C:\      ;
//      C:\foo\bar  -> C:\foo
//      C:\foo\     -> C:\foo
//      \\x\y\x     -> \\x\y
//      \\x\y       -> \\x
//      \\x         -> ?? (test this)
//      \foo        -> \  (Just the slash!)
//
// in/out:
//      pFile   fully qualified path name
// returns:
//      TRUE    we stripped something
//      FALSE   didn't strip anything (root directory case)
//
//      Stolen from shlwapi\path.c

STDAPI_(BOOL) _PathRemoveFileSpec(LPTSTR pFile)
{
    LPTSTR pT;
    LPTSTR pT2 = pFile;

    for (pT = pT2; *pT2; pT2 = CharNext(pT2)) {
        if (*pT2 == CH_WHACK)
            pT = pT2;             // last "\" found, (we will strip here)
        else if (*pT2 == TEXT(':')) {   // skip ":\" so we don't
            if (pT2[1] ==TEXT('\\'))    // strip the "\" from "C:\"
                pT2++;
            pT = pT2 + 1;
        }
    }
    if (*pT == 0)
        return FALSE;   // didn't strip anything

    //
    // handle the \foo case
    //
    else if ((pT == pFile) && (*pT == CH_WHACK)) {
        // Is it just a '\'?
        if (*(pT+1) != TEXT('\0')) {
            // Nope.
            *(pT+1) = TEXT('\0');
            return TRUE;        // stripped something
        }
        else        {
            // Yep.
            return FALSE;
        }
    }
    else {
        *pT = 0;
        return TRUE;    // stripped something
    }
}
