#include "private.h"
#include "subsmgrp.h"
#include "offl_cpp.h"
#include "helper.h"
#include "propshts.h"
#include "apithk.h"

#include <mluisupp.h>

#undef TF_THISMODULE
#define TF_THISMODULE TF_WEBCHECKCORE

extern HRESULT LoadSubscription(LPCTSTR url, LPMYPIDL *);
extern TCHAR szInternetSettings[];

extern void PropagateGeneralProp(HWND, POOEBuf);
extern INT_PTR CALLBACK EnableScreenSaverDlgProc(HWND, UINT, WPARAM, LPARAM);
extern HRESULT CreateSubscriptionFromOOEBuf(POOEBuf, LPMYPIDL *);

extern BOOL CALLBACK _AddOnePropSheetPage(HPROPSHEETPAGE, LPARAM);

extern TCHAR g_szDontAskScreenSaver[];    // from WIZARDS.CPP

#define MAX_STR_LENGTH 200

extern DWORD  aHelpIDs[];
extern TCHAR  c_szHelpFile[];

typedef struct
{
    CSubscriptionMgr* pMgr;
    LPCWSTR pwszName;
    LPCWSTR pwszUrl;
    SUBSCRIPTIONINFO* pSubsInfo;
    SUBSCRIPTIONTYPE subsType;
    DWORD dwFlags;
} SUBSCRIBE_ADI_INFO;

static const TCHAR SUBSCRIBEADIPROP[] = TEXT("SADIP");

INT_PTR CALLBACK SummarizeDesktopSubscriptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

////////////////////////////////////////////////////////////////////////////////
// Private imports from shdocvw (AddToFavorites API)
//
STDAPI SHAddSubscribeFavorite (HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszName, DWORD dwFlags,
                               SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO* pInfo);
////////////////////////////////////////////////////////////////////////////////


void UpdateSubsInfoFromOOE (SUBSCRIPTIONINFO* pInfo, POOEBuf pooe);

HRESULT CreateBSTRFromTSTR(BSTR * pBstr, LPCTSTR sz)
{
    int i = lstrlen(sz) + 1;
    *pBstr = SysAllocStringLen(NULL, i);
    if(NULL == *pBstr)
        return E_OUTOFMEMORY;

    MyStrToOleStrN(*pBstr, i, sz);
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
//                        Subsctiption Manager
//
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//
// constructor / destructor
//
CSubscriptionMgr::CSubscriptionMgr(void)
{
    ASSERT(NULL == _pidl);

    m_cRef = 1;
    m_eInitSrc = _INIT_FROM_URL;    //  Default.
    m_oldType = SUBSTYPE_URL;       //  Default.
    DllAddRef();
}

CSubscriptionMgr::~CSubscriptionMgr()
{
    if (_pidl)
    {
        COfflineFolderEnum::FreePidl(_pidl);
        _pidl = NULL;
    }
    SAFELOCALFREE(m_pBuf);
    SAFERELEASE(m_pUIHelper);
    DllRelease();
}

//
// IUnknown members
//
STDMETHODIMP CSubscriptionMgr::QueryInterface(REFIID riid, void ** ppv)
{
    *ppv=NULL;

    // Validate requested interface
    if ((IID_IUnknown == riid) ||
        (IID_ISubscriptionMgr == riid) ||
        (IID_ISubscriptionMgr2 == riid))
    {
        *ppv=(ISubscriptionMgr2 *)this;
    } 
    else if(IID_ISubscriptionMgrPriv == riid)
    {
        *ppv=(ISubscriptionMgrPriv *)this;
    }
    else if(IID_IShellExtInit == riid)
    {
        *ppv=(IShellExtInit *)this;
    }
    else if(IID_IShellPropSheetExt == riid)
    {
        *ppv=(IShellPropSheetExt *)this;
    }
    else
    {
        return E_NOINTERFACE;
    }

    ((LPUNKNOWN)*ppv)->AddRef();
    return S_OK;
}


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


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

    delete this;
    return 0L;
}


HRESULT CSubscriptionMgr::RemovePages(HWND hDlg)
{
    HRESULT hr;

    ASSERT(NULL != m_pUIHelper);

    if (NULL == m_pUIHelper)
    {
        return E_UNEXPECTED;
    }

    ISubscriptionAgentShellExt *psase;

    hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase);

    if (SUCCEEDED(hr))
    {
        hr = psase->RemovePages(hDlg);
        psase->Release();
    }

    return hr;
}

HRESULT CSubscriptionMgr::SaveSubscription()
{
    HRESULT hr;

    ASSERT(NULL != m_pUIHelper);

    if (NULL == m_pUIHelper)
    {
        return E_UNEXPECTED;
    }

    DWORD dwMaxCount = SHRestricted2W(REST_MaxSubscriptionCount, NULL, 0);
    DWORD dwCount;
    SUBSCRIPTIONTYPE subsType = (m_eInitSrc == _INIT_FROM_INTSHCUT) ? 
                                SUBSTYPE_URL : 
                                SUBSTYPE_CHANNEL;
    
    if ((dwMaxCount > 0) && 
        SUCCEEDED(CountSubscriptions(subsType, &dwCount)) &&
        (dwCount >= dwMaxCount))
    {
        SGMessageBox(GetForegroundWindow(), IDS_RESTRICTED, MB_OK);
        return E_ACCESSDENIED;
    }
    
    ISubscriptionAgentShellExt *psase;

    hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase);

    if (SUCCEEDED(hr))
    {
        hr = psase->SaveSubscription();
        psase->Release();
    }

    return hr;
}

HRESULT CSubscriptionMgr::URLChange(LPCWSTR pwszNewURL)
{
    return E_NOTIMPL;
}

HRESULT GetInfoFromDataObject(IDataObject *pido,
                              TCHAR *pszPath, DWORD cchPath,
                              TCHAR *pszFriendlyName, DWORD cchFriendlyName,
                              TCHAR *pszURL, DWORD cchURL,
                              INIT_SRC_ENUM *peInitSrc)
{
    STGMEDIUM stgmed;
    FORMATETC fmtetc = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
    HRESULT hr = pido->GetData(&fmtetc, &stgmed);

    if (hr == S_OK)
    {
        TCHAR szTempURL[INTERNET_MAX_URL_LENGTH];
        TCHAR *pszSection;
        TCHAR *pszEntry;
        TCHAR szTempPath[MAX_PATH];
        TCHAR szIniPath[MAX_PATH];

        if (DragQueryFile((HDROP)stgmed.hGlobal, 0, szTempPath, ARRAYSIZE(szTempPath)))
        {
            // save path
            if (NULL != pszPath)
            {
                StrCpyN(pszPath, szTempPath, cchPath);
            }

            StrCpyN(szIniPath, szTempPath, ARRAYSIZE(szIniPath));
            
            // make friendly name from path
            if (NULL != pszFriendlyName)
            {
                PathStripPath(szTempPath);
                PathRemoveExtension(szTempPath);
                StrCpyN(pszFriendlyName, szTempPath, cchFriendlyName);
            }

            if ((NULL != pszURL) || (NULL != peInitSrc))
            {

                if (PathIsDirectory(szIniPath))
                {
                    PathAppend(szIniPath, TEXT("desktop.ini"));
                    pszSection = TEXT("Channel");
                    pszEntry = TEXT("CDFURL");

                    if (NULL != peInitSrc)
                        *peInitSrc = _INIT_FROM_CHANNEL;
                }
                else
                {
                    pszSection = TEXT("InternetShortcut");
                    pszEntry = TEXT("URL");
                    
                    if (NULL != peInitSrc)
                        *peInitSrc = _INIT_FROM_INTSHCUT;
                }

                if (NULL != pszURL)
                {
                    // canonicalize url
                    if (SHGetIniString(pszSection, pszEntry,
                                                szTempURL, 
                                                INTERNET_MAX_URL_LENGTH, 
                                                szIniPath))
                    {
                        if(!InternetCanonicalizeUrl(szTempURL, pszURL, &cchURL, ICU_NO_ENCODE))
                        {
                            // failed - use non-canonical version
                            StrCpyN(pszURL, szTempURL, cchURL);
                        }
                    }
                    else
                    {
                        hr = E_FAIL;
                    }
                }
            }
        }
        else 
        {
            hr = E_FAIL;
        }

        ReleaseStgMedium(&stgmed);
    }

    return hr;
}

//
// IShellExtInit / IShellPropSheetExt members
//
STDMETHODIMP CSubscriptionMgr::Initialize(LPCITEMIDLIST pcidlFolder, IDataObject * pido, HKEY hkeyProgID)
{
    HRESULT hr;
    ISubscriptionItem *psi;
    CLSID clsid;
    SUBSCRIPTIONCOOKIE cookie;

    hr = GetInfoFromDataObject(pido, m_pszPath, ARRAYSIZE(m_pszPath),
                               m_pszFriendly, ARRAYSIZE(m_pszFriendly),
                               m_pszURL, ARRAYSIZE(m_pszURL),
                               &m_eInitSrc);

    if (SUCCEEDED(hr))
    {
        hr = DoGetItemFromURL(m_pszURL, &psi);
        
        if (SUCCEEDED(hr))
        {
            SUBSCRIPTIONITEMINFO sii;

            sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO);

            hr = psi->GetSubscriptionItemInfo(&sii);

            if (SUCCEEDED(hr))
            {
                clsid = sii.clsidAgent;
            }

            psi->GetCookie(&cookie);

            psi->Release();
        }

        if (FAILED(hr))
        {
            //  New subscription       
            hr = S_OK;
            CreateCookie(&cookie);

            switch (m_eInitSrc)
            {
                case _INIT_FROM_INTSHCUT:
                    clsid = CLSID_WebCrawlerAgent;
                    break;
                    
                case _INIT_FROM_CHANNEL:
                    clsid = CLSID_ChannelAgent;
                    break;

                default:
                    hr = E_FAIL;
                    break;
            }
        }

        if (SUCCEEDED(hr))
        {
            //  HACKHACK:
            //  We call coinit and uninit and keep an object pointer around.
            //  This ain't cool but will work as long as the agents are in
            //  webcheck.  Need to fix this for multi-cast handler.
            hr = CoInitialize(NULL);

            if (SUCCEEDED(hr))
            {
                hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER,
                                      IID_IUnknown, (void**)&m_pUIHelper);

                if (SUCCEEDED(hr))
                {
                    ISubscriptionAgentShellExt *psase;

                    hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase);
                    if (SUCCEEDED(hr))
                    {
                        WCHAR wszURL[ARRAYSIZE(m_pszURL)];
                        WCHAR wszName[MAX_NAME + 1];

                        MyStrToOleStrN(wszURL, ARRAYSIZE(wszURL), m_pszURL);
                        MyStrToOleStrN(wszName, ARRAYSIZE(wszName), m_pszFriendly);
                        
                        hr = psase->Initialize(&cookie, wszURL, wszName,
                                               (clsid == CLSID_ChannelAgent) ?
                                               SUBSTYPE_CHANNEL : SUBSTYPE_URL);
                        psase->Release();
                    }
                }
                CoUninitialize();
            }
        }
    }

    return hr;
}

STDMETHODIMP CSubscriptionMgr::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{
    HRESULT hr;
    if (SHRestricted2W(REST_NoEditingSubscriptions, NULL, 0))
        return E_FAIL;

    ASSERT(NULL != m_pUIHelper);

    if (NULL == m_pUIHelper)
    {
        return E_UNEXPECTED;
    }

    IShellPropSheetExt *pspse;

    hr = m_pUIHelper->QueryInterface(IID_IShellPropSheetExt, (void **)&pspse);

    if (SUCCEEDED(hr))
    {
        hr = pspse->AddPages(lpfnAddPage, lParam);
        pspse->Release();
    }

    return hr;
}

STDMETHODIMP CSubscriptionMgr::ReplacePage(UINT uPageID, LPFNADDPROPSHEETPAGE lpfnReplacePage, LPARAM lParam)
{
    return E_NOTIMPL;
}

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////

//
// ISubscriptionMgr members
//

STDMETHODIMP CSubscriptionMgr::IsSubscribed(LPCWSTR pURL, BOOL * pFSub)
{
    HRESULT hr;

    ASSERT (pURL && pFSub);
    MyOleStrToStrN(m_pszURL, INTERNET_MAX_URL_LENGTH, pURL);
 
    * pFSub = FALSE;

    if (!_pidl)
    {
        hr = LoadSubscription(m_pszURL, &_pidl);
        if ((hr != S_OK) || (!_pidl))
            return S_OK;
    }
    else if (UrlCompare(URL(&(_pidl->ooe)), m_pszURL, TRUE))
    {
        COfflineFolderEnum::FreePidl(_pidl);
        _pidl = NULL;
        hr = LoadSubscription(m_pszURL, &_pidl);
        if ((hr != S_OK) || (!_pidl))
            return S_OK;
    }

    * pFSub = TRUE;
    return S_OK;
}

STDMETHODIMP CSubscriptionMgr::DeleteSubscription(LPCWSTR pURL, HWND hwnd)
{
    ASSERT(pURL);
    MyOleStrToStrN(m_pszURL, INTERNET_MAX_URL_LENGTH, pURL);

    if (!_pidl)
    {
        HRESULT hr;

        hr = LoadSubscription(m_pszURL, &_pidl);
        if ((hr != S_OK) || (!_pidl))
            return E_FAIL;
    }

    // This is a restricted action.  The restriction
    // is checked in ConfirmDelete.  If you remove this call,
    // you must add the restriction check here.
    if (!ConfirmDelete(hwnd, 1, &_pidl))
        return E_FAIL;

    HRESULT hr = DoDeleteSubscription(&(_pidl->ooe));
    if (SUCCEEDED(hr))
    {
        TraceMsg(TF_ALWAYS, "%s(URL:%s) deleted", NAME(&(_pidl->ooe)), URL(&(_pidl->ooe)));

        _GenerateEvent(SHCNE_DELETE, (LPITEMIDLIST)_pidl, NULL);

        COfflineFolderEnum::FreePidl(_pidl);
        _pidl = NULL;
    }
    return hr;
}

STDMETHODIMP CSubscriptionMgr::ShowSubscriptionProperties(LPCWSTR pURL, HWND hwnd)
{
    HRESULT hr = S_OK;
    LPMYPIDL oldPidl = NULL, newPidl = NULL;

    ASSERT(pURL);
    MyOleStrToStrN(m_pszURL, INTERNET_MAX_URL_LENGTH, pURL);

    if (!m_pBuf)
    {
        m_pBuf = (OOEBuf *)MemAlloc(LPTR, sizeof(OOEBuf));
        if (NULL == m_pBuf)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr))
    {
        GetDefaultOOEBuf(m_pBuf, SUBSTYPE_URL);

        if(SUCCEEDED(LoadSubscription(m_pszURL, &oldPidl)))
        {
            POOEntry pooe = &(oldPidl->ooe);

            StrCpyN(m_pszFriendly, NAME(&(oldPidl->ooe)), ARRAYSIZE(m_pszFriendly));

            CopyToOOEBuf(&(oldPidl->ooe), m_pBuf);

            m_pBuf->m_dwPropSheetFlags = PSF_IS_ALREADY_SUBSCRIBED;
        }
        else
        {
            CreateCookie(&m_pBuf->m_Cookie);
            StrCpyN(m_pszFriendly, m_pszURL, ARRAYSIZE(m_pszFriendly));
            StrCpyN(m_pBuf->m_URL, m_pszURL, ARRAYSIZE(m_pBuf->m_URL));
            StrCpyN(m_pBuf->m_Name, m_pszURL, ARRAYSIZE(m_pBuf->m_Name));
        }

        hr = CoCreateInstance(*(&m_pBuf->clsidDest), NULL, CLSCTX_INPROC_SERVER, 
                              IID_IUnknown, (void **)&m_pUIHelper);

        if (SUCCEEDED(hr))
        {
            ISubscriptionAgentShellExt *psase;
            
            hr = m_pUIHelper->QueryInterface(IID_ISubscriptionAgentShellExt, (void **)&psase);

            if (SUCCEEDED(hr))
            {
                WCHAR wszURL[MAX_URL + 1];
                WCHAR wszName[MAX_NAME + 1];

                MyStrToOleStrN(wszURL, ARRAYSIZE(wszURL), m_pBuf->m_URL);
                MyStrToOleStrN(wszName, ARRAYSIZE(wszName), m_pBuf->m_Name);

                hr = psase->Initialize(&m_pBuf->m_Cookie, wszURL, wszName, (SUBSCRIPTIONTYPE)-1);
                psase->Release();
            }

            if (SUCCEEDED(hr))
            {
                PROPSHEETHEADER psh = { 0 } ;
                HPROPSHEETPAGE hPropPage[MAX_PROP_PAGES];

                // initialize propsheet header.
                psh.dwSize      = sizeof(PROPSHEETHEADER);
                psh.dwFlags     = PSH_PROPTITLE;
                psh.hwndParent  = hwnd;
                psh.pszCaption  = m_pszFriendly;
                psh.hInstance   = g_hInst;
                psh.nPages      = 0;
                psh.nStartPage  = 0;
                psh.phpage      = hPropPage;

                PROPSHEETPAGE psp;
                psp.dwSize          = sizeof(PROPSHEETPAGE);
                psp.dwFlags         = PSP_DEFAULT;
                psp.hInstance       = MLGetHinst();
                psp.pszIcon         = NULL;
                psp.pszTitle        = NULL;
                psp.lParam          = (LPARAM)this;

                psp.pszTemplate     = MAKEINTRESOURCE(IDD_SUBSPROPS_SUMMARY);
                psp.pfnDlgProc      = SummaryPropDlgProc;

                psh.phpage[psh.nPages++] = Whistler_CreatePropertySheetPageW(&psp);

                if (NULL != hPropPage[0])
                {
                    if (m_pBuf->m_dwPropSheetFlags & PSF_IS_ALREADY_SUBSCRIBED)
                    {
                        hr = AddPages(_AddOnePropSheetPage, (LPARAM)&psh);
                    }

                    if (SUCCEEDED(hr))
                    {
                        INT_PTR iRet = PropertySheet(&psh);

                        if (iRet < 0)
                        {
                            hr = E_FAIL;
                        }
                        else
                        {
                            hr = LoadSubscription(m_pszURL, &newPidl);
                            if (SUCCEEDED(hr))
                            {
                                if (_pidl)
                                {
                                    COfflineFolderEnum::FreePidl(_pidl);
                                }

                                _pidl = newPidl;
                            }
                        }
                    }
                }
            }
        }

        if (NULL != oldPidl)
        {
            COfflineFolderEnum::FreePidl(oldPidl);
        }
    }

    return hr;
}

//
//
//

void
CSubscriptionMgr::ChangeSubscriptionValues (
    OOEBuf *pCurrent,
    SUBSCRIPTIONINFO *pNew
)
{
    //
    // Channel flags
    //

    if (SUBSINFO_CHANNELFLAGS & pNew->fUpdateFlags)
    {
        pCurrent->fChannelFlags = pNew->fChannelFlags;
    }


    //
    // The subscription schedule.
    //

    if (SUBSINFO_SCHEDULE & pNew->fUpdateFlags)
    {

        switch (pNew->schedule)
        {

            case SUBSSCHED_DAILY:
            case SUBSSCHED_MANUAL:
            case SUBSSCHED_WEEKLY:
                LoadGroupCookie(&pCurrent->groupCookie, pNew->schedule);
                break;

            case SUBSSCHED_CUSTOM:
                pCurrent->groupCookie = pNew->customGroupCookie;
                break;

            case SUBSSCHED_AUTO:
                {
                    //  FEATURE. We should look at subType;
                    memset(&pCurrent->groupCookie, 0, sizeof(pCurrent->groupCookie));  //t-mattgi so it will look at the trigger
                    PTASK_TRIGGER pNewTrigger = ((PTASK_TRIGGER)pNew->pTrigger);
                    if (pNewTrigger && pNewTrigger->cbTriggerSize == sizeof(TASK_TRIGGER))
                    {
                        pCurrent->m_Trigger = *pNewTrigger;
                    }
                    else    //bad trigger; use daily as default
                    {
                        pCurrent->m_Trigger.cbTriggerSize = 0;
                        pCurrent->groupCookie = NOTFCOOKIE_SCHEDULE_GROUP_DAILY;
                    }
                }
                pCurrent->fChannelFlags |= CHANNEL_AGENT_DYNAMIC_SCHEDULE;
                break;

            default:
                ASSERT(FALSE);
                break;
        }
    }

    //
    // Recurse levels.
    //

    if (SUBSINFO_RECURSE & pNew->fUpdateFlags)
       pCurrent->m_RecurseLevels = pNew->dwRecurseLevels;

    //
    // Webcrawler flags.  Note:  The flags are not or'd with the current flags.
    // The caller must set all of the webcrawler flags they want to use.
    //

    if (SUBSINFO_WEBCRAWL & pNew->fUpdateFlags)
        pCurrent->m_RecurseFlags = pNew->fWebcrawlerFlags;

    //
    // Mail notification.
    //

    if (SUBSINFO_MAILNOT & pNew->fUpdateFlags)
        pCurrent->bMail = pNew->bMailNotification;
    else
        pCurrent->bMail = FALSE;

    //
    // Need password.
    //

    if (SUBSINFO_NEEDPASSWORD & pNew->fUpdateFlags)
        pCurrent->bNeedPassword = pNew->bNeedPassword;
    else
        pCurrent->bNeedPassword = FALSE;
    
    //
    // User name.
    //

    if (SUBSINFO_USER & pNew->fUpdateFlags)
    {
        if (pNew->bstrUserName)
        {
            MyOleStrToStrN(pCurrent->username, MAX_USERNAME, pNew->bstrUserName);
        }
        pCurrent->bNeedPassword = pNew->bNeedPassword;
    }
    
    //
    // Password.
    //

    if (SUBSINFO_PASSWORD & pNew->fUpdateFlags)
    {
        if (pNew->bstrPassword)
        {
            MyOleStrToStrN(pCurrent->password, MAX_PASSWORD, pNew->bstrPassword);
        }
        pCurrent->bNeedPassword = pNew->bNeedPassword;
    }

    //
    // Friendly Name.
    //

    if (SUBSINFO_FRIENDLYNAME & pNew->fUpdateFlags)
    {
        if (pNew->bstrFriendlyName)
        {
            MyOleStrToStrN(pCurrent->m_Name, MAX_NAME, pNew->bstrFriendlyName);
        }
    }

    //
    // Gleam
    //

    if (SUBSINFO_GLEAM & pNew->fUpdateFlags)
    {
        pCurrent->bGleam = pNew->bGleam;
    }

    //
    // Changes only (notification only)
    //

    if (SUBSINFO_CHANGESONLY & pNew->fUpdateFlags)
    {
        pCurrent->bChangesOnly = pNew->bChangesOnly;
    }

    //
    // dwMaxSizeKB
    //
    if (SUBSINFO_MAXSIZEKB & pNew->fUpdateFlags)
    {
        pCurrent->m_SizeLimit = pNew->dwMaxSizeKB;
    }

    //
    // Task flags
    //
    if (SUBSINFO_TASKFLAGS & pNew->fUpdateFlags)
    {
        pCurrent->grfTaskTrigger = pNew->fTaskFlags;
    }

    return;
}

//
// CSubscriptionMgr::CountSubscriptions
// FEATURE: We could make this public if other people need it.  An enumerator
// would be more useful though...
//
HRESULT CSubscriptionMgr::CountSubscriptions(SUBSCRIPTIONTYPE subType, PDWORD pdwCount)
{
    HRESULT hr;
    IEnumSubscription *pes;

    ASSERT(NULL != pdwCount);

    *pdwCount = 0;

    hr = EnumSubscriptions(0, &pes);

    if (SUCCEEDED(hr))
    {
        SUBSCRIPTIONCOOKIE cookie;

        while (S_OK == pes->Next(1, &cookie, NULL))
        {
            ISubscriptionItem *psi;
            DWORD dwRet;

            if (SUCCEEDED(SubscriptionItemFromCookie(FALSE, &cookie, &psi)))
            {
                if (SUCCEEDED(ReadDWORD(psi, c_szPropChannel, &dwRet)) && dwRet)
                {
                    if (SUBSTYPE_CHANNEL == subType)
                        (*pdwCount)++;
                }
                else if (SUCCEEDED(ReadDWORD(psi, c_szPropDesktopComponent, &dwRet)) && dwRet)
                {
                    if (SUBSTYPE_DESKTOPURL == subType || SUBSTYPE_DESKTOPCHANNEL == subType)
                        (*pdwCount)++;
                }
                else
                {
                    if (SUBSTYPE_URL == subType)
                        (*pdwCount)++;
                }
                psi->Release();
            }
        }
    }

    return hr;
}

//
// CSubscriptionMgr::IsValidSubscriptionInfo
//

#define SUBSCRIPTIONSCHEDULE_MAX 4

BOOL CSubscriptionMgr::IsValidSubscriptionInfo(SUBSCRIPTIONTYPE subType, SUBSCRIPTIONINFO *pSI)
{
    if (pSI->cbSize != sizeof(SUBSCRIPTIONINFO))
    {
        return FALSE;
    }
    else if (pSI->fUpdateFlags & ~SUBSINFO_ALLFLAGS)
    {
        return FALSE;
    }
    else if (pSI->pTrigger && ((TASK_TRIGGER*)(pSI->pTrigger))->cbTriggerSize &&
        (subType == SUBSTYPE_URL || subType == SUBSTYPE_DESKTOPURL)) // || pSI->schedule != SUBSSCHED_AUTO))
    {
        return FALSE;
    }
    else if (pSI->fUpdateFlags & SUBSINFO_SCHEDULE)
    {
        if (pSI->schedule > SUBSCRIPTIONSCHEDULE_MAX)
        {
            return FALSE;
        }
        if (pSI->schedule == SUBSSCHED_CUSTOM && pSI->customGroupCookie == CLSID_NULL)
        {
            return FALSE;
        }
    }

    return TRUE;
}

////////////////////////////////////////////////////////////////////////////////
//
// *** RemoveURLMapping ***
//
// Description:
//     Removes the cache and registry settings that wininet uses to map the 
//     given url to a local file.
//
////////////////////////////////////////////////////////////////////////////////

#define PRELOAD_REG_KEY \
    TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Preload")

void RemoveURLMapping(LPCWSTR pszURL)
{
    BYTE cei[MY_MAX_CACHE_ENTRY_INFO];
    DWORD cbcei = sizeof(cei);
    LPINTERNET_CACHE_ENTRY_INFOW pcei = (LPINTERNET_CACHE_ENTRY_INFOW)cei;
    
    //
    // Look up the url in the cache
    //
    if (GetUrlCacheEntryInfoExW(pszURL, pcei, &cbcei, NULL, 0, NULL, 0))
    {
        // 
        // see if it has a mapping because it is a preinstalled cache entry
        //
        if (pcei->CacheEntryType & INSTALLED_CACHE_ENTRY)
        {
            //
            // Clear the flag
            //
            pcei->CacheEntryType &= ~INSTALLED_CACHE_ENTRY;
            SetUrlCacheEntryInfoW(pszURL, pcei, CACHE_ENTRY_ATTRIBUTE_FC);

            //
            // Now remove the mapping from the registry
            //
            HKEY hk;
            if (RegOpenKeyEx(HKEY_CURRENT_USER, PRELOAD_REG_KEY, 0, KEY_WRITE, &hk) == ERROR_SUCCESS) 
            {
                RegDeleteValueW(hk, pszURL);
                RegCloseKey(hk);
            }
        }
    }
}

//
// CSubscriptionMgr::CreateSubscription
// entry point for creating a subscription
// flags:
//    CREATESUBS_FROMFAVORITES -- already exists in favorites, use alternate summary dialog
//                         that doesn't do AddToFavorites.  Valid only for channel or url.
//    CREATESUBS_INACTIVEPLATINUM -- when creating a channel subscription, show Activate Channel dialog
//                         valid only for channel subscriptions with CREATESUBS_FROMFAVORITES
//    CREATESUBS_ADDTOFAVORITES -- display summary dialog before wizard
//        default summary: for channel or url, use AddToFavorites from shdocvw
//                         for desktop item, just a confirmation dialog
//                         for other, no summary -- straight to wizard
//    CREATESUBS_NOUI -- totally silent
//    CREATESUBS_NOSAVE -- update subscription in memory buffer, not on disk (pInfo must be non-NULL)
//
STDMETHODIMP
CSubscriptionMgr::CreateSubscription (
    HWND hwnd,
    LPCWSTR pwszURL,
    LPCWSTR pwszFriendlyName,
    DWORD dwFlags,
    SUBSCRIPTIONTYPE subsType,
    SUBSCRIPTIONINFO *pInfo
)
{
    HRESULT hr = E_INVALIDARG;
    BOOL bAlready;

    if (IsFlagSet(dwFlags, CREATESUBS_NOUI) || !IsFlagSet (dwFlags, CREATESUBS_ADDTOFAVORITES))
    {    //no UI, so skip ATF dialog
        hr = CreateSubscriptionNoSummary (hwnd, pwszURL, pwszFriendlyName, dwFlags,
            subsType, pInfo);

        if (hr == S_OK)
        {
            //
            // The user successfully subscribed to this URL so remove
            // mappings used for preinstalled content as this URL is now
            // "Activated"
            //
            RemoveURLMapping(pwszURL);
        }
    }
    else
    {
        switch (subsType)
        {
        case SUBSTYPE_URL:
        case SUBSTYPE_CHANNEL:
            hr = SHAddSubscribeFavorite (hwnd, pwszURL, pwszFriendlyName,
                                         dwFlags, subsType, pInfo);
            break;

        case SUBSTYPE_DESKTOPCHANNEL:
        case SUBSTYPE_DESKTOPURL:
            hr = IsSubscribed (pwszURL, &bAlready);
            if (SUCCEEDED (hr) && bAlready)
                break;  //don't display summary dialog since it has nothing useful for this case
            hr = CreateDesktopSubscription (hwnd, pwszURL, pwszFriendlyName,
                                            dwFlags, subsType, pInfo);
            break;

        default:    //SUBSTYPE_EXTERNAL -- don't know what kind of summary to show
            hr = CreateSubscriptionNoSummary (hwnd, pwszURL, pwszFriendlyName, dwFlags,
                subsType, pInfo);
            break;
        }
    }

    return hr;
}


//
// CSubscriptionMgr::CreateSubscriptionNoSummary
// modify a SUBSCRIPTIONINFO interactively, using the wizard
// persists info to Subscriptions folder, unless SUBSINFO_NOSAVE passed
//
STDMETHODIMP
CSubscriptionMgr::CreateSubscriptionNoSummary (
    HWND hwnd,
    LPCWSTR pwszURL,
    LPCWSTR pwszFriendlyName,
    DWORD dwFlags,
    SUBSCRIPTIONTYPE subType,
    SUBSCRIPTIONINFO *pInfo
)
{
    HRESULT hr = S_OK;
    
    //
    // Validate the parameters.
    //
    if (!IS_VALID_SUBSCRIPTIONTYPE(subType)
        || !pwszURL
        || !pwszFriendlyName
        || (!pInfo && (dwFlags & CREATESUBS_NOSAVE))
        || (pInfo && !IsValidSubscriptionInfo(subType, pInfo)))
    {
        ASSERT(FALSE);
        return E_INVALIDARG;
    }

    //
    // Fail if already subscribed and we aren't in no save or no UI mode.
    // Caller is responsible for UI.
    //
    BOOL fAlreadySubscribed;
    if ((FAILED(IsSubscribed(pwszURL, &fAlreadySubscribed)) || fAlreadySubscribed) &&
        (!(dwFlags & (CREATESUBS_NOSAVE | CREATESUBS_NOUI))))
    {
        return E_FAIL;
    }

    //
    // Fail if restrictions are in place.  
    // FEATURE: Currently cdfview is handling channel restrictions for 
    // but we should probably do it here.
    // Should we have a flag parameter to override this?
    //
    if (SUBSTYPE_URL == subType)
    {
        DWORD dwMaxCount = SHRestricted2W(REST_MaxSubscriptionCount, NULL, 0);
        DWORD dwCount;
        if (SHRestricted2W(REST_NoAddingSubscriptions, pwszURL, 0)
            || ((dwMaxCount > 0)
                && SUCCEEDED(CountSubscriptions(subType, &dwCount))
                && (dwCount >= dwMaxCount)))
        {
            if (!IsFlagSet(dwFlags, CREATESUBS_NOUI))
                SGMessageBox(hwnd, IDS_RESTRICTED, MB_OK);
            return E_ACCESSDENIED;
        }
    }
    
    //
    // Get the subscription defaults and merge in the caller's info
    //
    OOEBuf subProps;
    GetDefaultOOEBuf(&subProps, subType);
    
    //this is (intentionally) duplicated below... it needs to be after the ChangeSubscriptionValues()
    //call, but we need to grab the url first to make sure it's subscribable.
    MyOleStrToStrN(subProps.m_URL, INTERNET_MAX_URL_LENGTH, pwszURL);

    // Does this mean we can't support plugin protocols?
    if (/*(subType != SUBSTYPE_EXTERNAL) &&*/ !IsHTTPPrefixed(subProps.m_URL))
    {
        return E_INVALIDARG;
    }

    if (pInfo)
    {
        ChangeSubscriptionValues(&subProps, pInfo);
        if (fAlreadySubscribed)
        {
            ReadCookieFromInetDB(subProps.m_URL, &subProps.m_Cookie);
            subProps.m_dwPropSheetFlags |= PSF_IS_ALREADY_SUBSCRIBED;
        }
    }

    // Disallow password caching if restriction is in place.  This both
    // skips the wizard page and prevents the caller's password from
    // being saved.
    if (SHRestricted2W(REST_NoSubscriptionPasswords, NULL, 0))
    {
        subProps.bNeedPassword = FALSE;
        subProps.username[0] = 0;
        subProps.password[0] = 0;
        subProps.dwFlags &= ~(PROP_WEBCRAWL_UNAME | PROP_WEBCRAWL_PSWD);
    }

    //the passed-in name and url override whatever's in the info buffer
    MyOleStrToStrN(subProps.m_URL, INTERNET_MAX_URL_LENGTH, pwszURL);
    MyOleStrToStrN(subProps.m_Name, MAX_NAME_QUICKLINK, pwszFriendlyName);

    //
    // If we're in UI mode, initialize the wizard
    //
    if (!IsFlagSet(dwFlags, CREATESUBS_NOUI))
    {
        hr = CreateWizard(hwnd, subType, &subProps);

    } // !NOUI

    //
    // If we're not in NOSAVE mode, then create/save the subscription
    //
    if (SUCCEEDED(hr))
    {
        if (!IsFlagSet(dwFlags, CREATESUBS_NOSAVE))
        {
            //hack to let AddToFavorites dialog display the screen-saver-activate dialog -- it
            //needs to pass NOUI, which disables all UI, but if you also pass FROMFAVORITES and not
            //ADDTOFAVORITES then we still let this dialog slip through.  (Shouldn't affect anyone
            //else because FROMFAVORITES is always used with ADDTOFAVORITES.)
            if (!IsFlagSet(dwFlags, CREATESUBS_NOUI) ||
                (IsFlagSet(dwFlags, CREATESUBS_FROMFAVORITES) && !IsFlagSet (dwFlags, CREATESUBS_ADDTOFAVORITES)))
            {
                // See if the user wants to be asked about enabling the screen saver.
                DWORD dwValue = BST_UNCHECKED;
                ReadRegValue(   HKEY_CURRENT_USER,
                                WEBCHECK_REGKEY,
                                g_szDontAskScreenSaver,
                                &dwValue,
                                SIZEOF(dwValue));

                if  (
                    (subProps.fChannelFlags & CHANNEL_AGENT_PRECACHE_SCRNSAVER)
                    &&
                    (dwValue == BST_UNCHECKED)
                    &&
                    !IsADScreenSaverActive()
                    )
                {
                    DialogBoxParam( MLGetHinst(), 
                             MAKEINTRESOURCE(IDD_SUBSCRIPTION_ENABLECHANNELSAVER),
                             hwnd,
                             EnableScreenSaverDlgProc,
                             0L);
                }
            }

            //
            // Create a new pidl with the user specified properties.
            //
            if (_pidl)
            {
                COfflineFolderEnum::FreePidl(_pidl);
                _pidl = NULL;
                SAFERELEASE(m_pUIHelper);
            }
            hr = CreateSubscriptionFromOOEBuf(&subProps, &_pidl);
            if (SUCCEEDED(hr))
            {
                ASSERT(_pidl);
                //
                // Send a notification that a subscription has changed.
                //
                _GenerateEvent(SHCNE_CREATE, (LPITEMIDLIST)_pidl, NULL);
            }
        } //!NOSAVE
        else if (S_OK == hr)
        {
            //in NOSAVE mode, so don't actually create subscription -- save it back
            //to passed-in buffer
            ASSERT (pInfo);
            pInfo->fUpdateFlags = SUBSINFO_ALLFLAGS;    //fill in all possible fields
            UpdateSubsInfoFromOOE (pInfo, &subProps);
        }
    }
    
    return hr;
}


STDMETHODIMP
CSubscriptionMgr::CreateDesktopSubscription (HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszFriendlyName,
                        DWORD dwFlags, SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO *pInfo)
{
    HRESULT hr;
    SUBSCRIPTIONINFO siTemp = { sizeof(SUBSCRIPTIONINFO), 0 };
    if (!pInfo)
        pInfo = &siTemp;    //make sure we have a valid buffer if caller doesn't give us one

    //make sure adminrestrictions allow this

    if (SHRestricted2W(REST_NoAddingChannels, pwszURL, 0))
        return E_FAIL;

    SUBSCRIBE_ADI_INFO parms = { this, pwszFriendlyName, pwszURL, pInfo, subsType, dwFlags };

    //make sure this url is subscribable; if not, show error dialog
    {
        TCHAR sz[MAX_URL];
        MyOleStrToStrN (sz, ARRAYSIZE(sz), pwszURL);

        if (!IsHTTPPrefixed (sz))
        {
            SGMessageBox(hwnd, IDS_HTTPONLY, MB_ICONINFORMATION | MB_OK);
            return E_INVALIDARG;
        }
    }

    INT_PTR iDlgResult = DialogBoxParam (MLGetHinst(), MAKEINTRESOURCE(IDD_DESKTOP_SUBSCRIPTION_SUMMARY),
                        hwnd, SummarizeDesktopSubscriptionDlgProc, (LPARAM)&parms);

    switch (iDlgResult)
    {
    case -1:
        hr = E_FAIL;
        break;
    case IDCANCEL:
        hr = S_FALSE;
        break;
    default:
        hr = CreateSubscriptionNoSummary (hwnd, pwszURL, pwszFriendlyName,
                CREATESUBS_NOUI | dwFlags, subsType, pInfo);
        break;
    }

    return hr;
}


STDMETHODIMP
CSubscriptionMgr::GetDefaultInfo(
    SUBSCRIPTIONTYPE    subType,
    SUBSCRIPTIONINFO *pInfo
)
{
    //
    // Validate the parameters.
    //
    if (!IS_VALID_SUBSCRIPTIONTYPE(subType)
        || !pInfo 
        || (pInfo->cbSize != sizeof(SUBSCRIPTIONINFO)))
    {
        ASSERT(FALSE);
        return E_INVALIDARG;
    }

    memset((void *)pInfo, 0, sizeof(SUBSCRIPTIONINFO));
    pInfo->cbSize = sizeof(SUBSCRIPTIONINFO);

    // Fill in default structure.  Note that lines are commented out
    // below to indicate the field is initialized to 0 without wasting
    // code (memset above already cleared structure out.)
    
    pInfo->fUpdateFlags = SUBSINFO_RECURSE | SUBSINFO_MAILNOT 
                        | SUBSINFO_WEBCRAWL 
                        /*| SUBSINFO_SCHEDULE */ | SUBSINFO_CHANGESONLY
                        | SUBSINFO_CHANNELFLAGS;
    pInfo->dwRecurseLevels = DEFAULTLEVEL;
    pInfo->schedule = SUBSSCHED_AUTO;
    
    switch (subType)
    {
        case SUBSTYPE_URL:
//            pInfo->bChangesOnly = FALSE;
//            pInfo->bMailNotification = FALSE;
//            pInfo->bPasswordNeeded = FALSE;
            pInfo->fWebcrawlerFlags = DEFAULTFLAGS;
            break;

        case SUBSTYPE_CHANNEL:
//            pInfo->bChangesOnly = FALSE;
//            pInfo->bMailNotification = FALSE;
            pInfo->fChannelFlags = CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_DYNAMIC_SCHEDULE;
            break;

        case SUBSTYPE_DESKTOPCHANNEL:
//          pInfo->bChangesOnly = FALSE;
//          pInfo->bMailNotification = FALSE;
            pInfo->fChannelFlags = CHANNEL_AGENT_PRECACHE_ALL | CHANNEL_AGENT_DYNAMIC_SCHEDULE;
            break;
            
        case SUBSTYPE_DESKTOPURL:
//          pInfo->bChangesOnly = FALSE;
//          pInfo->bMailNotification = FALSE;
            pInfo->fWebcrawlerFlags = DEFAULTFLAGS;
            break;
            
        default:
            return E_NOTIMPL;
    }
    
    return S_OK;
}

STDMETHODIMP
CSubscriptionMgr::GetSubscriptionInfo(
    LPCWSTR pwszURL,
    SUBSCRIPTIONINFO *pInfo
)
{
    HRESULT hr;

    //
    // Validate the parameters.
    //
    if (!pInfo 
        || !pwszURL
        || (pInfo->cbSize != sizeof(SUBSCRIPTIONINFO)))
    {
        ASSERT(FALSE);
        return E_INVALIDARG;
    }

    BOOL    bSubscribe;
    hr = IsSubscribed(pwszURL, &bSubscribe);

    RETURN_ON_FAILURE(hr);
    if (!bSubscribe)
    {
        return E_FAIL;
    }


    // We cannot rely on the caller passing us a clean SUBSCRIPTIONINFO
    // structure.  We need to clean it ourselves.
    DWORD dwFlags = pInfo->fUpdateFlags;
    ZeroMemory(pInfo, sizeof(SUBSCRIPTIONINFO));
    pInfo->cbSize = sizeof(SUBSCRIPTIONINFO);
    pInfo->fUpdateFlags = dwFlags;

    OOEBuf ooeb;    // Alas, we need the code in UpdateSubsInfoFromOOE
    CopyToOOEBuf (&(_pidl->ooe), &ooeb);    //to work once for a buf and once for an entry, and it's
    UpdateSubsInfoFromOOE (pInfo, &ooeb);   //easier to convert entry->buf so we do that here.

    return S_OK;
}


void UpdateSubsInfoFromOOE (SUBSCRIPTIONINFO* pInfo, POOEBuf pooe)
{
    DWORD   dwFlags = pInfo->fUpdateFlags & SUBSINFO_ALLFLAGS;
    SUBSCRIPTIONTYPE subType = GetItemCategory(pooe);

    if (dwFlags & SUBSINFO_USER)
    {
        SAFEFREEBSTR (pInfo->bstrUserName);
        CreateBSTRFromTSTR(&(pInfo->bstrUserName), pooe->username);
    }
    if (dwFlags & SUBSINFO_PASSWORD)
    {
        SAFEFREEBSTR (pInfo->bstrPassword);
        CreateBSTRFromTSTR(&(pInfo->bstrPassword), pooe->password);
    }
    if (dwFlags & SUBSINFO_FRIENDLYNAME)
    {
        SAFEFREEBSTR (pInfo->bstrFriendlyName);
        CreateBSTRFromTSTR(&(pInfo->bstrFriendlyName), pooe->m_Name);
    }
    
    pInfo->fUpdateFlags = dwFlags;
    if (dwFlags & SUBSINFO_SCHEDULE)
    {
        pInfo->schedule = GetGroup(pooe);
        if (pInfo->schedule == SUBSSCHED_CUSTOM)
        {
            if (pooe->groupCookie != GUID_NULL)
            {
                pInfo->customGroupCookie = pooe->groupCookie;
            }
            else
            {
                GetItemSchedule(&pooe->m_Cookie, &pInfo->customGroupCookie);
                if (pInfo->customGroupCookie == GUID_NULL)
                {
                    pInfo->schedule = SUBSSCHED_MANUAL;
                }
            }
        }
    }
    
    if (PTASK_TRIGGER pInfoTrigger = (PTASK_TRIGGER)pInfo->pTrigger)
    {
        if (pInfoTrigger->cbTriggerSize == pooe->m_Trigger.cbTriggerSize)
            *(pInfoTrigger) = pooe->m_Trigger;
        else
            pInfoTrigger->cbTriggerSize = 0;
    }
    //otherwise, it's already null and we can't do anything about it... luckily, we'll never
    //have a trigger that we need to write back to a SUBSCRIPTIONINFO that didn't already have
    //one.

    if (dwFlags & SUBSINFO_RECURSE)
        pInfo->dwRecurseLevels = pooe->m_RecurseLevels;
    if (dwFlags & SUBSINFO_WEBCRAWL)
        pInfo->fWebcrawlerFlags = pooe->m_RecurseFlags;
    if (dwFlags & SUBSINFO_MAILNOT)
        pInfo->bMailNotification = pooe->bMail;
    if (dwFlags & SUBSINFO_GLEAM)
        pInfo->bGleam = pooe->bGleam;
    if (dwFlags & SUBSINFO_CHANGESONLY)
        pInfo->bChangesOnly = pooe->bChangesOnly;
    if (dwFlags & SUBSINFO_NEEDPASSWORD)
        pInfo->bNeedPassword = pooe->bNeedPassword;
    if (dwFlags & SUBSINFO_CHANNELFLAGS)
    {
        if ((subType==SUBSTYPE_CHANNEL)||(subType==SUBSTYPE_DESKTOPCHANNEL))
        {
            pInfo->fChannelFlags = pooe->fChannelFlags;
        }
        else
        {
            pInfo->fChannelFlags = 0;
            pInfo->fUpdateFlags &= (~SUBSINFO_CHANNELFLAGS);
        }
    }
    if (dwFlags & SUBSINFO_MAXSIZEKB)
        pInfo->dwMaxSizeKB = pooe->m_SizeLimit;


    if (dwFlags & SUBSINFO_TYPE)
    {
        pInfo->subType = GetItemCategory(pooe);
        ASSERT(IS_VALID_SUBSCRIPTIONTYPE(pInfo->subType));
    }

    if (dwFlags & SUBSINFO_TASKFLAGS)
    {
        pInfo->fTaskFlags = pooe->grfTaskTrigger;
    }

}


STDMETHODIMP
CSubscriptionMgr::UpdateSubscription(LPCWSTR pwszURL)
{
    ASSERT(pwszURL);
    BOOL    bSubscribe = FALSE;
    HRESULT hr = IsSubscribed(pwszURL, &bSubscribe);
    CLSID clsId;

    RETURN_ON_FAILURE(hr);
    if (!bSubscribe)
    {
        return E_INVALIDARG;
    }

    //
    // Fail if restrictions are in place.  
    // FEATURE: Should we have a flag parameter to override this?
    //
    if (SHRestricted2W(REST_NoManualUpdates, NULL, 0))
    {
        SGMessageBox(NULL, IDS_RESTRICTED, MB_OK);
        return E_ACCESSDENIED;
    }

    clsId = _pidl->ooe.m_Cookie;    
    hr = SendUpdateRequests(NULL, &clsId, 1);
    _pidl->ooe.m_Cookie = clsId;

    return hr;
}

STDMETHODIMP
CSubscriptionMgr::UpdateAll()
{
    //
    // Fail if restrictions are in place.  
    // FEATURE: Should we have a flag parameter to override this?
    //
    if (SHRestricted2W(REST_NoManualUpdates, NULL, 0))
    {
        SGMessageBox(NULL, IDS_RESTRICTED, MB_OK);
        return E_ACCESSDENIED;
    }

    return SendUpdateRequests(NULL, NULL, 0);
}

HRESULT MergeOOEBuf(POOEBuf p1, POOEBuf p2, DWORD fMask)
{
    ASSERT(p1 && p2);
    DWORD   dwMask = p2->dwFlags & fMask;

    if (dwMask == 0)
        return S_OK;

    if (p1->clsidDest != p2->clsidDest)
        return E_INVALIDARG;

    if (dwMask & PROP_WEBCRAWL_COOKIE)
    {
        //  We shouldn't merge cookies.
    }

    if (dwMask & PROP_WEBCRAWL_SIZE)
    {
        p1->m_SizeLimit = p2->m_SizeLimit;
    }
    if (dwMask & PROP_WEBCRAWL_FLAGS)
    {
        p1->m_RecurseFlags = p2->m_RecurseFlags;
    }
    if (dwMask & PROP_WEBCRAWL_LEVEL)
    {
        p1->m_RecurseLevels = p2->m_RecurseLevels;
    }
    if (dwMask & PROP_WEBCRAWL_URL)
    {
        StrCpyN(p1->m_URL, p2->m_URL, MAX_URL);
    }
    if (dwMask & PROP_WEBCRAWL_NAME)
    {
        StrCpyN(p1->m_Name, p2->m_Name, MAX_NAME);
    }

    if (dwMask & PROP_WEBCRAWL_PSWD)
    {
        StrCpyN(p1->password, p2->password, MAX_PASSWORD);
    }
    if (dwMask & PROP_WEBCRAWL_UNAME)
    {
        StrCpyN(p1->username, p2->username, MAX_USERNAME);
    }
    if (dwMask & PROP_WEBCRAWL_DESKTOP)
    {
        p1->bDesktop = p2->bDesktop;
    }
    if (dwMask & PROP_WEBCRAWL_CHANNEL)
    {
        p1->bChannel = p2->bChannel;
    }
    if (dwMask & PROP_WEBCRAWL_EMAILNOTF)
    {
        p1->bMail = p2->bMail;
    }
    if (dwMask & PROP_WEBCRAWL_RESCH)
    {
        p1->grfTaskTrigger = p2->grfTaskTrigger;
        p1->groupCookie = p2->groupCookie;
        p1->fChannelFlags |= (p2->fChannelFlags & CHANNEL_AGENT_DYNAMIC_SCHEDULE);
    }
    if (dwMask & PROP_WEBCRAWL_LAST)
    {
        p1->m_LastUpdated = p2->m_LastUpdated;
    }
    if (dwMask & PROP_WEBCRAWL_STATUS)
    {
        p1->status = p2->status;
    }
    if (dwMask & PROP_WEBCRAWL_PRIORITY)
    {
        p1->m_Priority = p2->m_Priority;
    }
    if (dwMask & PROP_WEBCRAWL_GLEAM)
    {
        p1->bGleam = p2->bGleam;
    }
    if (dwMask & PROP_WEBCRAWL_CHANGESONLY)
    {
        p1->bChangesOnly = p2->bChangesOnly;
    }
    if (dwMask & PROP_WEBCRAWL_CHANNELFLAGS)
    {
        p1->fChannelFlags = p2->fChannelFlags;
    }

    p1->dwFlags |= (p2->dwFlags & fMask & (~PROP_WEBCRAWL_COOKIE));

    return S_OK;
}


INT_PTR CALLBACK SummarizeDesktopSubscriptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    SUBSCRIBE_ADI_INFO* pInfo = (SUBSCRIBE_ADI_INFO*)GetProp(hDlg,SUBSCRIBEADIPROP);

    switch (uMsg)
    {
    case WM_INITDIALOG:
        pInfo = (SUBSCRIBE_ADI_INFO*)lParam;
        ASSERT (pInfo);
        SetProp (hDlg, SUBSCRIBEADIPROP, (HANDLE)pInfo);
        {   //block to declare vars to update captions
            TCHAR sz[MAX_URL];

            if (pInfo->subsType == SUBSTYPE_DESKTOPCHANNEL)
            {
                if(MLLoadString(
                    (pInfo->pSubsInfo->bNeedPassword ? IDS_DESKTOPCHANNEL_SUMMARY_TEXT : IDS_DESKTOPCHANNEL_SUMMARY_NOPW),
                    sz, ARRAYSIZE(sz)))
                {
                    SetDlgItemText(hDlg, IDC_DESKTOP_SUMMARY_TEXT, sz);
                }
            }

            MyOleStrToStrN (sz, ARRAYSIZE(sz), pInfo->pwszName);
            SetListViewToString(GetDlgItem(hDlg, IDC_SUBSCRIBE_ADI_NAME), sz);
            MyOleStrToStrN (sz, ARRAYSIZE(sz), pInfo->pwszUrl);
            SetListViewToString (GetDlgItem (hDlg, IDC_SUBSCRIBE_ADI_URL), sz);
        }
        break;

    case WM_COMMAND:
        ASSERT (pInfo);
        switch (GET_WM_COMMAND_ID(wParam, lParam))
        {
        case IDCANCEL:
            EndDialog(hDlg, IDCANCEL);
            break;

        case IDOK:
            //subscription happens in calling function when we return IDOK
            EndDialog(hDlg, IDOK);
            break;

        case IDC_SUBSCRIBE_CUSTOMIZE:
            //run through wizard in NOSAVE mode
            if (pInfo->pMgr &&
                S_OK == pInfo->pMgr->CreateSubscriptionNoSummary (hDlg, pInfo->pwszUrl,
                                        pInfo->pwszName, pInfo->dwFlags | CREATESUBS_NOSAVE,
                                        pInfo->subsType, pInfo->pSubsInfo))
            {
                SendMessage (hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDOK), TRUE);
            }
            break;
        }
        break;

        case WM_NOTIFY:
            if (LOWORD(wParam) == IDC_SUBSCRIBE_ADI_URL)
            {
                NM_LISTVIEW * pnmlv = (NM_LISTVIEW *)lParam;
                if (pnmlv->hdr.code == LVN_GETINFOTIP)
                {
                    TCHAR szURL[MAX_URL];
                    LV_ITEM lvi = {0};
                    lvi.mask = LVIF_TEXT;
                    lvi.pszText = szURL;
                    lvi.cchTextMax = ARRAYSIZE(szURL);
                    if (!ListView_GetItem (GetDlgItem (hDlg, IDC_SUBSCRIBE_ADI_URL), &lvi))
                        return FALSE;

                    NMLVGETINFOTIP  * pTip = (NMLVGETINFOTIP *)pnmlv;
                    ASSERT(pTip);
                    StrCpyN(pTip->pszText, szURL, pTip->cchTextMax);
                    return TRUE;
                }
            }
        break;

    case WM_DESTROY:
        RemoveProp (hDlg, SUBSCRIBEADIPROP);
        break;
    }

    return FALSE;
}