#include "private.h"
#include "subitem.h"
#include "subsmgrp.h"

#include "helper.h"
#include "offl_cpp.h"   // Yech. Pidl stuff.

const TCHAR c_szSubscriptionInfoValue[] = TEXT("~SubsInfo");

#ifdef UNICODE
#define c_wszSubscriptionInfoValue c_szSubscriptionInfoValue
#else
const WCHAR c_wszSubscriptionInfoValue[] = L"~SubsInfo";
#endif

//  pragmas are for compatibility with the notification manager
//  registry data structures which were not pack 8

#pragma pack(push, RegVariantBlob, 1)

//#pragma pack(8)
struct NT32PACKAGE
{
    unsigned _int16 vt; /* VARTYPE *//* unsigned short int */
    unsigned _int16 wReserved1; /* WORD *//* unsigned short int */
    unsigned _int16 wReserved2; /* WORD *//* unsigned short int */
    unsigned _int16 wReserved3; /* WORD *//* unsigned short int */

   _int64 llVal; /* LONGLONG *//* __int64 */
};

// Q: What's going on here?
// A: Not a whole lot.
//    We used to store a variant in the registry. Unfortunately, variants are 
//    16 bytes on Win32 (8 byte header + 8 bytes of data)
//    24 bytes on Win64 (8 byte header + 16 bytes of data)

// Unfortunately, Win64 webcheck and Win32 webcheck both read from
// the same registry location, i.e. the same blob, and won't understand one another.
// Thus, boom! At least, for BSTRs that are stored inline

// Fortunately, we care only about only the first 16 bytes on both platforms. 
// Ergo, it's sufficient to store only the top half of the Win64 variant.

struct SimpleVariantBlob
{
#ifdef OLD
    VARIANT var;
#else
    NT32PACKAGE var;
#endif
};

struct BSTRVariantBlob : public SimpleVariantBlob
{
    DWORD   dwSize;
//    WCHAR   wsz[];    //  Variable length string
};

struct OldBSTRVariantBlob
{
    DWORD   dwSize;
    VARIANT var;
};


struct SignatureSimpleBlob
{
    DWORD               dwSignature;
    SimpleVariantBlob   svb;
};

struct SignatureBSTRBlob
{
    DWORD               dwSignature;
    BSTRVariantBlob     bvb;
};

#pragma pack(pop, RegVariantBlob)


#define BLOB_SIGNATURE 0x4b434f4d

// We need fStream to indicate when we're upgrading IE4-style streams-of-blobs. (IE6 24398)
HRESULT BlobToVariant(BYTE *pData, DWORD cbData, VARIANT *pVar, DWORD *pcbUsed, BOOL fStream)
{
    HRESULT hr = S_OK;
    SimpleVariantBlob *pBlob = (SimpleVariantBlob *)pData;

    ASSERT(NULL != pBlob);
    ASSERT(cbData >= sizeof(SimpleVariantBlob));
    ASSERT(NULL != pVar);

    if ((NULL != pBlob) &&
        (cbData >= sizeof(SimpleVariantBlob)) &&
        (NULL != pVar))
    {
#ifdef OLD
        memcpy(pVar, &pBlob->var, sizeof(VARIANT));
#else
        memcpy(pVar, &pBlob->var, sizeof(NT32PACKAGE));
#endif

        switch (pVar->vt)
        {
            case VT_I4:                 // LONG           
            case VT_UI1:                // BYTE           
            case VT_I2:                 // SHORT          
            case VT_R4:                 // FLOAT          
            case VT_R8:                 // DOUBLE         
            case VT_BOOL:               // VARIANT_BOOL   
            case VT_ERROR:              // SCODE          
            case VT_CY:                 // CY             
            case VT_DATE:               // DATE
            case VT_I1:                 // CHAR           
            case VT_UI2:                // USHORT         
            case VT_UI4:                // ULONG          
            case VT_INT:                // INT            
            case VT_UINT:               // UINT           
                if (pcbUsed)
                {
                    *pcbUsed = sizeof(SimpleVariantBlob);
                }
                break;                

            case VT_BSTR:               // BSTR
                hr = E_UNEXPECTED;

                ASSERT(cbData >= sizeof(BSTRVariantBlob));

                if (cbData >= sizeof(BSTRVariantBlob))
                {
                    BSTRVariantBlob *pbstrBlob = (BSTRVariantBlob *)pData;
                    DWORD dwSize = pbstrBlob->dwSize;

#ifdef OLD
                    ASSERT(cbData>=(sizeof(BSTRVariantBlob) + dwSize));

                    if (cbData>=(sizeof(BSTRVariantBlob) + dwSize))
#else
#ifdef WIN64
                    ASSERT((cbData==(sizeof(BSTRVariantBlob) + dwSize)) 
                           || (cbData==(sizeof(OldBSTRVariantBlob) + dwSize)));
#else
                    ASSERT((cbData==(sizeof(BSTRVariantBlob) + dwSize))
                            || (fStream && (cbData>=(sizeof(BSTRVariantBlob) + dwSize))));

#endif
                    if ((cbData==(sizeof(BSTRVariantBlob) + dwSize))
                        || (fStream && (cbData>=(sizeof(BSTRVariantBlob) + dwSize))))
#endif
                    {
                        pVar->bstrVal = SysAllocStringByteLen(NULL, dwSize);

                        if (NULL != pVar->bstrVal)
                        {
                            if (pcbUsed)
                            {
                                *pcbUsed = sizeof(BSTRVariantBlob) + pbstrBlob->dwSize;
                            }
                            memcpy(pVar->bstrVal, 
                                   ((BYTE *)pbstrBlob) + 
                                        (FIELD_OFFSET(BSTRVariantBlob, dwSize) + 
                                        sizeof(dwSize)),
                                   dwSize);
                            hr = S_OK;
                        }
                        else
                        {
                            hr = E_OUTOFMEMORY;
                        }
                    }
                }
                if (FAILED(hr))
                {
                    pVar->vt = VT_EMPTY;
                }
                break;                

            default:
                hr = E_NOTIMPL;
                break;
        }
    }
    else
    {
        hr = E_UNEXPECTED;
    }

    return hr;
}

HRESULT SignatureBlobToVariant(BYTE *pData, DWORD cbData, VARIANT *pVar)
{
    HRESULT hr;
    
    SignatureSimpleBlob *pBlob = (SignatureSimpleBlob *)pData;

    ASSERT(NULL != pBlob);
    ASSERT(cbData >= sizeof(SignatureSimpleBlob));
    ASSERT(NULL != pVar);
    ASSERT(BLOB_SIGNATURE == pBlob->dwSignature);

    if ((NULL != pBlob) &&
        (cbData >= sizeof(SignatureSimpleBlob)) &&
        (NULL != pVar) &&
        (BLOB_SIGNATURE == pBlob->dwSignature))
    {
        hr = BlobToVariant((BYTE *)&pBlob->svb, 
                           cbData - (FIELD_OFFSET(SignatureSimpleBlob, svb)),
                           pVar,
                           NULL);
    }
    else
    {
        hr = E_UNEXPECTED;
    }

    return hr;
}

HRESULT VariantToSignatureBlob(const VARIANT *pVar, BYTE **ppData, DWORD *pdwSize)
{
    HRESULT hr;

    ASSERT(NULL != pVar);
    ASSERT(NULL != ppData);
    ASSERT(NULL != pdwSize);

    if ((NULL != pVar) && (NULL != ppData) && (NULL != pdwSize))
    {
        DWORD dwSize;
        DWORD dwBstrLen = 0;

        hr = S_OK;
     
        switch (pVar->vt)
        {
            case VT_I4:                 // LONG           
            case VT_UI1:                // BYTE           
            case VT_I2:                 // SHORT          
            case VT_R4:                 // FLOAT          
            case VT_R8:                 // DOUBLE         
            case VT_BOOL:               // VARIANT_BOOL   
            case VT_ERROR:              // SCODE          
            case VT_CY:                 // CY             
            case VT_DATE:               // DATE
            case VT_I1:                 // CHAR           
            case VT_UI2:                // USHORT         
            case VT_UI4:                // ULONG          
            case VT_INT:                // INT            
            case VT_UINT:               // UINT           
                dwSize = sizeof(SignatureSimpleBlob);
                break;                

            case VT_BSTR:               // BSTR
                if (NULL != pVar->bstrVal) 
                    dwBstrLen = SysStringByteLen(pVar->bstrVal);
                dwSize = sizeof(SignatureBSTRBlob) + dwBstrLen;
                break;
                        
            default:
                hr = E_NOTIMPL;
                dwSize = 0;
                break;
        }

        if (SUCCEEDED(hr))
        {
            SignatureSimpleBlob *pSignatureBlob = (SignatureSimpleBlob *)new BYTE[dwSize];

            if (NULL != pSignatureBlob)
            {
                *ppData = (BYTE *)pSignatureBlob;
                *pdwSize = dwSize;
                
                pSignatureBlob->dwSignature = BLOB_SIGNATURE;

                switch (pVar->vt)
                {
                    case VT_I4:                 // LONG           
                    case VT_UI1:                // BYTE           
                    case VT_I2:                 // SHORT          
                    case VT_R4:                 // FLOAT          
                    case VT_R8:                 // DOUBLE         
                    case VT_BOOL:               // VARIANT_BOOL   
                    case VT_ERROR:              // SCODE          
                    case VT_CY:                 // CY             
                    case VT_DATE:               // DATE
                    case VT_I1:                 // CHAR           
                    case VT_UI2:                // USHORT         
                    case VT_UI4:                // ULONG          
                    case VT_INT:                // INT            
                    case VT_UINT:               // UINT
                    {
                        SimpleVariantBlob *pBlob = &pSignatureBlob->svb;

#ifdef OLD
                        memcpy(&pBlob->var, pVar, sizeof(VARIANT));
#else
                        memcpy(&pBlob->var, pVar, sizeof(NT32PACKAGE));
#endif
                        break;
                    }

                    case VT_BSTR:               // BSTR
                    {
                        BSTRVariantBlob *pbstrBlob = 
                            &((SignatureBSTRBlob *)pSignatureBlob)->bvb;
                        
#ifdef OLD
                        memcpy(&pbstrBlob->var, pVar, sizeof(VARIANT));
#else
                        memcpy(&pbstrBlob->var, pVar, sizeof(NT32PACKAGE));
#endif
                        pbstrBlob->dwSize = dwBstrLen;
                        memcpy(((BYTE *)pbstrBlob) + 
                                  (FIELD_OFFSET(BSTRVariantBlob, dwSize) + 
                                  sizeof(dwSize)),
                               pVar->bstrVal, 
                               dwBstrLen);
                        break;
                    }
                                
                    default:
                        ASSERT(0);  // Default case should have been eliminated!
                        break;
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
    }
    else        
    {
        hr = E_INVALIDARG;
    }

    return hr;
}

CEnumItemProperties::CEnumItemProperties()
{
    ASSERT(0 == m_nCurrent);
    ASSERT(0 == m_nCount);
    ASSERT(NULL == m_pItemProps);

    m_cRef = 1;

    DllAddRef();    
}

CEnumItemProperties::~CEnumItemProperties()
{
    if (NULL != m_pItemProps)
    {
        for (ULONG i = 0; i < m_nCount; i++)
        {
            VariantClear(&m_pItemProps[i].variantValue);
            if (NULL != m_pItemProps[i].pwszName)
            {
                CoTaskMemFree(m_pItemProps[i].pwszName);
            }
        }
        delete [] m_pItemProps;
    }
    DllRelease();
}

HRESULT CEnumItemProperties::Initialize(const SUBSCRIPTIONCOOKIE *pCookie, ISubscriptionItem *psi)
{
    HRESULT hr = E_FAIL;
    HKEY hkey;

    ASSERT(NULL != pCookie);

    if (OpenItemKey(pCookie, FALSE, KEY_READ, &hkey))
    {
        DWORD dwMaxValNameSize;
        DWORD dwMaxDataSize;
        DWORD dwCount;

        if (RegQueryInfoKey(hkey,
            NULL,   // address of buffer for class string 
            NULL,   // address of size of class string buffer 
            NULL,   // reserved 
            NULL,   // address of buffer for number of subkeys 
            NULL,   // address of buffer for longest subkey name length  
            NULL,   // address of buffer for longest class string length 
            &dwCount,   // address of buffer for number of value entries 
            &dwMaxValNameSize,  // address of buffer for longest value name length 
            &dwMaxDataSize, // address of buffer for longest value data length 
            NULL,   // address of buffer for security descriptor length 
            NULL    // address of buffer for last write time
            ) == ERROR_SUCCESS)
        {
            //  This allocates enough for Password as well
            m_pItemProps = new ITEMPROP[dwCount];

            dwMaxValNameSize++; //  Need room for NULL

            //  alloca candidates:

            TCHAR *pszValName = new TCHAR[dwMaxValNameSize];
#ifndef UNICODE
            WCHAR *pwszValName = new WCHAR[dwMaxValNameSize];
#endif
            BYTE *pData = new BYTE[dwMaxDataSize];  
            
            if ((NULL != m_pItemProps) && (NULL != pData) && 
#ifndef UNICODE
                (NULL != pwszValName) &&
#endif
                (NULL != pszValName) 
               )
            {
                hr = S_OK;

                for (ULONG i = 0; i < dwCount; i++)
                {
                    DWORD dwType;
                    DWORD dwSize = dwMaxDataSize;
                    DWORD dwNameSize = dwMaxValNameSize;

                    if (SHEnumValue(hkey, i, pszValName, &dwNameSize, 
                                    &dwType, pData, &dwSize) != ERROR_SUCCESS)
                    {
                        hr = E_UNEXPECTED;
                        break;
                    }

                    //  Skip the default value and our subscription info structure
                    if ((NULL == *pszValName) ||
                        (0 == StrCmp(pszValName, c_szSubscriptionInfoValue))
                       )
                    {
                        continue;
                    }

                    if (dwType != REG_BINARY)
                    {
                        hr = E_UNEXPECTED;
                        break;
                    }

                    hr = SignatureBlobToVariant(pData, dwSize, &m_pItemProps[m_nCount].variantValue);
                    if (SUCCEEDED(hr))
                    {
                        WCHAR *pwszName;
                    #ifdef UNICODE
                        pwszName = pszValName;
                    #else
                        MultiByteToWideChar(CP_ACP, 0, pszValName, -1, 
                                    pwszValName, dwMaxValNameSize);
                        pwszName = pwszValName;
                    #endif                           
                        ULONG ulSize = (lstrlenW(pwszName) + 1) * sizeof(WCHAR);
                        m_pItemProps[m_nCount].pwszName = (WCHAR *)CoTaskMemAlloc(ulSize);
                        if (NULL != m_pItemProps[m_nCount].pwszName)
                        {
                            StrCpyW(m_pItemProps[m_nCount].pwszName, pwszName);
                        }
                        else
                        {
                            hr = E_OUTOFMEMORY;
                            break;
                        }
                        m_nCount++;
                    }
                    else
                    {
                        break;
                    }
                }

                if (SUCCEEDED(ReadPassword(psi, &m_pItemProps[m_nCount].variantValue.bstrVal)))
                {
                    m_pItemProps[m_nCount].pwszName = (WCHAR *)CoTaskMemAlloc(sizeof(L"Password"));

                    if (NULL != m_pItemProps[m_nCount].pwszName)
                    {
                        StrCpyW(m_pItemProps[m_nCount].pwszName, L"Password");
                        m_pItemProps[m_nCount].variantValue.vt = VT_BSTR;
                        m_nCount++;
                    }
                    else
                    {
                        SysFreeString(m_pItemProps[m_nCount].variantValue.bstrVal);
                    }
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        #ifndef UNICODE
            SAFEDELETE(pwszValName);
        #endif
            SAFEDELETE(pszValName);
            SAFEDELETE(pData);
        }
        RegCloseKey(hkey);
    }
    return hr;
}

// IUnknown members
STDMETHODIMP CEnumItemProperties::QueryInterface(REFIID riid, void **ppv)
{
    HRESULT hr;

    if (NULL == ppv)
    {
        return E_INVALIDARG;
    }

    if ((IID_IUnknown == riid) || (IID_IEnumItemProperties == riid))
    {
        *ppv = (IEnumItemProperties *)this;
        AddRef();
        hr = S_OK;
    }
    else
    {
        *ppv = NULL;
        hr = E_NOINTERFACE;
    }
    
    return hr;
}

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

STDMETHODIMP_(ULONG) CEnumItemProperties::Release()
{
    if (--m_cRef == 0)
    {
        delete this;
        return 0;
    }

    return m_cRef;
}

HRESULT CEnumItemProperties::CopyItem(ITEMPROP *pip, WCHAR *pwszName, VARIANT *pVar)
{
    HRESULT hr;

    ASSERT(NULL != pwszName);
    
    if (NULL != pwszName)
    {
        
        ULONG cb = (lstrlenW(pwszName) + 1) * sizeof(WCHAR);

        pip->pwszName = (WCHAR *)CoTaskMemAlloc(cb);
        if (NULL != pip->pwszName)
        {
            StrCpyW(pip->pwszName, pwszName);
            pip->variantValue.vt = VT_EMPTY;    // is this a good idea?
            hr = VariantCopy(&pip->variantValue, pVar);
        }
        else
        {
            hr = E_OUTOFMEMORY;
        }
    }
    else
    {
        hr = E_UNEXPECTED;
    }

    return hr;
}

HRESULT CEnumItemProperties::CopyRange(ULONG nStart, ULONG nCount, 
                                       ITEMPROP *ppip, ULONG *pnCopied)
{
    HRESULT hr = S_OK;
    ULONG n = 0;
    ULONG i;

    ASSERT((NULL != ppip) && (NULL != pnCopied));
    
    for (i = nStart; (S_OK == hr) && (i < m_nCount) && (n < nCount); i++, n++)
    {
        hr = CopyItem(&ppip[n], m_pItemProps[i].pwszName, &m_pItemProps[i].variantValue);
    }

    *pnCopied = n;

    if (SUCCEEDED(hr))
    {
        hr = (n == nCount) ? S_OK : S_FALSE;
    }
    
    return hr;
}

// IEnumItemProperties
STDMETHODIMP CEnumItemProperties::Next( 
    /* [in] */ ULONG celt,
    /* [length_is][size_is][out] */ ITEMPROP *rgelt,
    /* [out] */ ULONG *pceltFetched)
{
    HRESULT hr;

    if ((0 == celt) || 
        ((celt > 1) && (NULL == pceltFetched)) ||
        (NULL == rgelt))
    {
        return E_INVALIDARG;
    }

    DWORD nFetched;

    hr = CopyRange(m_nCurrent, celt, rgelt, &nFetched);

    m_nCurrent += nFetched;

    if (pceltFetched)
    {
        *pceltFetched = nFetched;
    }

    return hr;
}

STDMETHODIMP CEnumItemProperties::Skip( 
    /* [in] */ ULONG celt)
{
    HRESULT hr;
    
    m_nCurrent += celt;

    if (m_nCurrent > (m_nCount - 1))
    {
        m_nCurrent = m_nCount;  //  Passed the last one
        hr = S_FALSE;
    }
    else
    {
        hr = S_OK;
    }
    
    return hr;
}

STDMETHODIMP CEnumItemProperties::Reset()
{
    m_nCurrent = 0;

    return S_OK;
}

STDMETHODIMP CEnumItemProperties::Clone( 
    /* [out] */ IEnumItemProperties **ppenum)
{
    HRESULT hr = E_OUTOFMEMORY;

    *ppenum = NULL;

    CEnumItemProperties *peip = new CEnumItemProperties;

    if (NULL != peip)
    {
        peip->m_pItemProps = new ITEMPROP[m_nCount];

        if (NULL != peip->m_pItemProps)
        {
            ULONG nFetched;

            hr = E_FAIL;

            peip->m_nCount = m_nCount;
            hr = CopyRange(0, m_nCount, peip->m_pItemProps, &nFetched);

            if (SUCCEEDED(hr))
            {
                ASSERT(m_nCount == nFetched);

                if (m_nCount == nFetched)
                {
                    hr = peip->QueryInterface(IID_IEnumItemProperties, (void **)ppenum);
                }
            }
        }
        peip->Release();
    }    
    return hr;
}

STDMETHODIMP CEnumItemProperties::GetCount( 
    /* [out] */ ULONG *pnCount)
{
    if (NULL == pnCount)
    {
        return E_INVALIDARG;
    }

    *pnCount = m_nCount;

    return S_OK;
}


CSubscriptionItem::CSubscriptionItem(const SUBSCRIPTIONCOOKIE *pCookie, HKEY hkey)
{
    ASSERT(NULL != pCookie);
    ASSERT(0 == m_dwFlags);

    m_cRef = 1;

    if (NULL != pCookie)
    {
        m_Cookie = *pCookie;
    }

    SUBSCRIPTIONITEMINFO sii;

    sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);

    if ((hkey != NULL) && 
        SUCCEEDED(Read(hkey, c_wszSubscriptionInfoValue, (BYTE *)&sii, sizeof(SUBSCRIPTIONITEMINFO))))

    {
        m_dwFlags = sii.dwFlags;
    }

    DllAddRef();
}

CSubscriptionItem::~CSubscriptionItem()
{
    if (m_dwFlags & SI_TEMPORARY)
    {
        TCHAR szKey[MAX_PATH];

        if (ItemKeyNameFromCookie(&m_Cookie, szKey, ARRAYSIZE(szKey)))
        {
             SHDeleteKey(HKEY_CURRENT_USER, szKey);
        }
    }
    DllRelease();
}

HRESULT CSubscriptionItem::Read(HKEY hkeyIn, const WCHAR *pwszValueName, 
                                BYTE *pData, DWORD dwDataSize)
{
    HRESULT hr = E_FAIL;
    HKEY hkey = hkeyIn;

    ASSERT((NULL != pwszValueName) && (NULL != pData) && (0 != dwDataSize));

    if ((NULL != hkey) || OpenItemKey(&m_Cookie, FALSE, KEY_READ, &hkey))
    {
        DWORD dwType;
        DWORD dwSize = dwDataSize;

#ifdef UNICODE
        if ((RegQueryValueExW(hkey, pwszValueName, NULL, &dwType, pData, &dwSize) == ERROR_SUCCESS) &&
#else
        TCHAR szValueName[MAX_PATH];
        WideCharToMultiByte(CP_ACP, 0, pwszValueName, -1, szValueName, ARRAYSIZE(szValueName), NULL, NULL);
        if ((RegQueryValueExA(hkey, szValueName, NULL, &dwType, pData, &dwSize) == ERROR_SUCCESS) &&
#endif
            (dwSize == dwDataSize) && (REG_BINARY == dwType))
        {
            hr = S_OK;
        }
        if (NULL == hkeyIn)
        {
            RegCloseKey(hkey);
        }
    }
    return hr;
}

HRESULT CSubscriptionItem::ReadWithAlloc(HKEY hkeyIn, const WCHAR *pwszValueName, 
                                         BYTE **ppData, DWORD *pdwDataSize)
{
    HRESULT hr = E_FAIL;
    HKEY hkey = hkeyIn;

    ASSERT((NULL != pwszValueName) && (NULL != ppData) && (NULL != pdwDataSize));

    if ((NULL != hkey) || OpenItemKey(&m_Cookie, FALSE, KEY_READ, &hkey))
    {
        DWORD dwType;
        DWORD dwSize = 0;

#ifdef UNICODE
        if (RegQueryValueExW(hkey, pwszValueName, NULL, &dwType, NULL, &dwSize) == ERROR_SUCCESS)
#else
        TCHAR szValueName[MAX_PATH];
        WideCharToMultiByte(CP_ACP, 0, pwszValueName, -1, szValueName, ARRAYSIZE(szValueName), NULL, NULL);
        if (RegQueryValueExA(hkey, szValueName, NULL, &dwType, NULL, &dwSize) == ERROR_SUCCESS)
#endif
        {
            BYTE *pData = new BYTE[dwSize];
            *pdwDataSize = dwSize;

            if (NULL != pData)
            {
#ifdef UNICODE
                if ((RegQueryValueExW(hkey, pwszValueName, NULL, &dwType, pData, pdwDataSize) == ERROR_SUCCESS) &&
#else
                if ((RegQueryValueExA(hkey, szValueName, NULL, &dwType, pData, pdwDataSize) == ERROR_SUCCESS) &&
#endif
                    (dwSize == *pdwDataSize) && (REG_BINARY == dwType))
                {
                    *ppData = pData;
                    hr = S_OK;
                }
                else
                {
                    delete [] pData;
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
        if (NULL == hkeyIn)
        {
            RegCloseKey(hkey);
        }
    }
    return hr;
}

HRESULT CSubscriptionItem::Write(HKEY hkeyIn, const WCHAR *pwszValueName, 
                                 BYTE *pData, DWORD dwDataSize)
{
    HRESULT hr = E_FAIL;
    HKEY hkey = hkeyIn;

    ASSERT((NULL != pwszValueName) && (NULL != pData) && (0 != dwDataSize));

    if ((NULL != hkey) || OpenItemKey(&m_Cookie, FALSE, KEY_WRITE, &hkey))
    {
#ifdef UNICODE
        if (RegSetValueExW(hkey, pwszValueName, 0, REG_BINARY, pData, dwDataSize) == ERROR_SUCCESS)
#else
        TCHAR szValueName[MAX_PATH];
        WideCharToMultiByte(CP_ACP, 0, pwszValueName, -1, szValueName, ARRAYSIZE(szValueName), NULL, NULL);
        if (RegSetValueExA(hkey, szValueName, 0, REG_BINARY, pData, dwDataSize) == ERROR_SUCCESS)
#endif
        {
            hr = S_OK;
        }
        if (NULL == hkeyIn)
        {
            RegCloseKey(hkey);
        }
    }
    return hr;
}


STDMETHODIMP CSubscriptionItem::QueryInterface(REFIID riid, void **ppv)
{
    HRESULT hr;

    if (NULL == ppv)
    {
        return E_INVALIDARG;
    }

    if ((IID_IUnknown == riid) || (IID_ISubscriptionItem == riid))
    {
        *ppv = (ISubscriptionItem *)this;
        AddRef();
        hr = S_OK;
    }
    else
    {
        *ppv = NULL;
        hr = E_NOINTERFACE;
    }
    
    return hr;
}


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

STDMETHODIMP_(ULONG) CSubscriptionItem::Release()
{
    if (--m_cRef == 0)
    {
        delete this;
        return 0;
    }

    return m_cRef;
}

STDMETHODIMP CSubscriptionItem::GetCookie(SUBSCRIPTIONCOOKIE *pCookie)
{
    if (NULL == pCookie)
    {
        return E_INVALIDARG;
    }

    *pCookie = m_Cookie;

    return S_OK;
}

STDMETHODIMP CSubscriptionItem::GetSubscriptionItemInfo( 
    /* [out] */ SUBSCRIPTIONITEMINFO *pSubscriptionItemInfo)
{
    HRESULT hr;

    if ((NULL == pSubscriptionItemInfo) ||
        (pSubscriptionItemInfo->cbSize < sizeof(SUBSCRIPTIONITEMINFO)))
    {
        return E_INVALIDARG;
    }

    hr = Read(NULL, c_wszSubscriptionInfoValue, (BYTE *)pSubscriptionItemInfo, sizeof(SUBSCRIPTIONITEMINFO));

    ASSERT(sizeof(SUBSCRIPTIONITEMINFO) == pSubscriptionItemInfo->cbSize);
    
    if (SUCCEEDED(hr) &&
        (sizeof(SUBSCRIPTIONITEMINFO) != pSubscriptionItemInfo->cbSize))
    {
        hr = E_UNEXPECTED;
    }

    return hr;
}

STDMETHODIMP CSubscriptionItem::SetSubscriptionItemInfo( 
    /* [in] */ const SUBSCRIPTIONITEMINFO *pSubscriptionItemInfo)
{
    if ((NULL == pSubscriptionItemInfo) ||
        (pSubscriptionItemInfo->cbSize < sizeof(SUBSCRIPTIONITEMINFO)))
    {
        return E_INVALIDARG;
    }

    m_dwFlags = pSubscriptionItemInfo->dwFlags;
    
    return Write(NULL, c_wszSubscriptionInfoValue, (BYTE *)pSubscriptionItemInfo, sizeof(SUBSCRIPTIONITEMINFO));
}

STDMETHODIMP CSubscriptionItem::ReadProperties( 
    ULONG nCount,
    /* [size_is][in] */ const LPCWSTR rgwszName[],
    /* [size_is][out] */ VARIANT rgValue[])
{
    HRESULT hr = S_OK;
    
    if ((0 == nCount) || (NULL == rgwszName) || (NULL == rgValue))
    {
        return E_INVALIDARG;
    }

    HKEY hkey;

    if (OpenItemKey(&m_Cookie, FALSE, KEY_READ, &hkey))
    {
        for (ULONG i = 0; (i < nCount) && (S_OK == hr); i++)
        {
            BYTE *pData;
            DWORD dwDataSize;

            if (StrCmpIW(rgwszName[i], c_szPropPassword) == 0)
            {
                if (SUCCEEDED(ReadPassword(this, &rgValue[i].bstrVal)))
                {
                    rgValue[i].vt = VT_BSTR;
                }
                else
                {
                    rgValue[i].vt = VT_EMPTY;
                }
            }
            else
            {
                HRESULT hrRead = ReadWithAlloc(hkey, rgwszName[i], &pData, &dwDataSize);

                if (SUCCEEDED(hrRead))
                {
                    hr = SignatureBlobToVariant(pData, dwDataSize, &rgValue[i]);
                    delete [] pData;
                }
                else
                {
                    rgValue[i].vt = VT_EMPTY;
                }
            }
        }
        RegCloseKey(hkey);
    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}

STDMETHODIMP CSubscriptionItem::WriteProperties( 
    ULONG nCount,
    /* [size_is][in] */ const LPCWSTR rgwszName[],
    /* [size_is][in] */ const VARIANT rgValue[])
{
    HRESULT hr = S_OK;
    
    if ((0 == nCount) || (NULL == rgwszName) || (NULL == rgValue))
    {
        return E_INVALIDARG;
    }

    HKEY hkey;

    if (OpenItemKey(&m_Cookie, FALSE, KEY_WRITE, &hkey))
    {
        for (ULONG i = 0; (i < nCount) && (S_OK == hr); i++)
        {
            if (rgValue[i].vt == VT_EMPTY)
            {
                //  We don't actually care if this fails since it is
                //  meant to delete the property anyhow
#ifdef UNICODE
                RegDeleteValueW(hkey, rgwszName[i]);
#else
                TCHAR szValueName[MAX_PATH];
                WideCharToMultiByte(CP_ACP, 0, rgwszName[i], -1, szValueName, 
                                    ARRAYSIZE(szValueName), NULL, NULL);
                RegDeleteValueA(hkey, szValueName);
#endif
            }
            else
            {
                BYTE *pData;
                DWORD dwDataSize;

                //  Special case the name property for easy viewing
                if ((VT_BSTR == rgValue[i].vt) && 
                    (StrCmpIW(rgwszName[i], c_szPropName) == 0))
                {   
                #ifdef UNICODE
                    RegSetValueExW(hkey, NULL, 0, REG_SZ, (LPBYTE)rgValue[i].bstrVal, 
                                   (lstrlenW(rgValue[i].bstrVal) + 1) * sizeof(WCHAR));
                #else
                    TCHAR szValueName[MAX_PATH];
                    WideCharToMultiByte(CP_ACP, 0, rgValue[i].bstrVal, -1, szValueName, 
                                        ARRAYSIZE(szValueName), NULL, NULL);
                    RegSetValueExA(hkey, NULL, 0, REG_SZ, (LPBYTE)szValueName, lstrlenA(szValueName) + 1);
                #endif
                }

                if (StrCmpIW(rgwszName[i], c_szPropPassword) == 0)
                {
                    if (VT_BSTR == rgValue[i].vt)
                    {
                        hr = WritePassword(this, rgValue[i].bstrVal);
                    }
                    else
                    {
                        hr = E_INVALIDARG;
                    }
                }
                else
                {
                    hr = VariantToSignatureBlob(&rgValue[i], &pData, &dwDataSize);

                    if (SUCCEEDED(hr))
                    {
                        hr = Write(hkey, rgwszName[i], pData, dwDataSize);
                        delete [] pData;
                    }
                }
            }
        }
        RegCloseKey(hkey);
    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}

STDMETHODIMP CSubscriptionItem::EnumProperties( 
    /* [out] */ IEnumItemProperties **ppEnumItemProperties)
{
    HRESULT hr;

    if (NULL == ppEnumItemProperties)
    {
        return E_INVALIDARG;
    }
    CEnumItemProperties *peip = new CEnumItemProperties;

    *ppEnumItemProperties = NULL;

    if (NULL != peip)
    {
        hr = peip->Initialize(&m_Cookie, this);
        if (SUCCEEDED(hr))
        {
            hr = peip->QueryInterface(IID_IEnumItemProperties, (void **)ppEnumItemProperties);
        }
        peip->Release();
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    return hr;
}

STDMETHODIMP CSubscriptionItem::NotifyChanged()
{
    HRESULT hr;

    // Notify the shell folder of the updated item
    // This is SOMEWHAT inefficient... why do we need 1000 properties for a pidl?
    // Why do we copy them around? Why why why?

    OOEBuf      ooeBuf;
    LPMYPIDL    newPidl = NULL;
    DWORD       dwSize = 0;

    memset(&ooeBuf, 0, sizeof(ooeBuf));

    hr = LoadWithCookie(NULL, &ooeBuf, &dwSize, &m_Cookie);

    if (SUCCEEDED(hr))
    {
        newPidl = COfflineFolderEnum::NewPidl(dwSize);
        if (newPidl)
        {
            CopyToMyPooe(&ooeBuf, &(newPidl->ooe));
            _GenerateEvent(SHCNE_UPDATEITEM, (LPITEMIDLIST)newPidl, NULL);
            COfflineFolderEnum::FreePidl(newPidl);
        }
    }

    return hr;
}
