//----------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997
//
//  File:       admin.cpp
//
//  Contents:   
//
//  Classes:
//
//  Functions:
//
//  History:    
//
//----------------------------------------------------------------------------

#include "private.h"
#include "shguidp.h"
#include "chanmgr.h"
#include "chanmgrp.h"
#include "winineti.h"

#include <mluisupp.h>

// Infodelivery Policies registry locations
#define INFODELIVERY_POLICIES TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Infodelivery")
// const TCHAR c_szRegKeyRestrictions[]    = INFODELIVERY_POLICIES TEXT("\\Restrictions");
const TCHAR c_szRegKeyModifications[]   = INFODELIVERY_POLICIES TEXT("\\Modifications");
const TCHAR c_szRegKeyCompletedMods[]   = INFODELIVERY_POLICIES TEXT("\\CompletedModifications");
const TCHAR c_szRegKeyIESetup[]         = TEXT("Software\\Microsoft\\IE4\\Setup");

// Wininet cache preload directory
const TCHAR c_szRegKeyCachePreload[]    = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Preload");

// Registry key names of supported Modifications
const TCHAR c_szAddChannels[]               = TEXT("AddChannels");
const TCHAR c_szRemoveChannels[]            = TEXT("RemoveChannels");
const TCHAR c_szRemoveAllChannels[]         = TEXT("RemoveAllChannels");
const TCHAR c_szAddSubscriptions[]          = TEXT("AddSubscriptions");
const TCHAR c_szRemoveSubscriptions[]       = TEXT("RemoveSubscriptions");
const TCHAR c_szAddScheduleGroups[]         = TEXT("AddScheduleGroups");
const TCHAR c_szRemoveScheduleGroups[]      = TEXT("RemoveScheduleGroups");
const TCHAR c_szAddDesktopComponents[]      = TEXT("AddDesktopComponents");
const TCHAR c_szRemoveDesktopComponents[]   = TEXT("RemoveDesktopComponents");

// Registry value names of supported Modifications
const TCHAR c_szURL[]                   = TEXT("URL");
const TCHAR c_szTitle[]                 = TEXT("Title");
const TCHAR c_szLogo[]                  = TEXT("Logo");
const TCHAR c_szWideLogo[]              = TEXT("WideLogo");
const TCHAR c_szIcon[]                  = TEXT("Icon");
const TCHAR c_szCategory[]              = TEXT("Category");
const TCHAR c_szChannelGuide[]          = TEXT("ChannelGuide"); // DO NOTE CHANGE THIS STRING WITHOUT UPDATING CDFVIEW!!!
const TCHAR c_szPreloadURL[]            = TEXT("PreloadURL");
const TCHAR c_szLCID[]                  = TEXT("LangId");       // This must be an LCID despite its name
const TCHAR c_szSoftware[]              = TEXT("Software");
const TCHAR c_szSubscriptionType[]      = TEXT("SubscriptionType");
const TCHAR c_szScheduleGroup[]         = TEXT("ScheduleGroup");
const TCHAR c_szEarliestTime[]          = TEXT("EarliestTime");
const TCHAR c_szIntervalTime[]          = TEXT("IntervalTime");
const TCHAR c_szLatestTime[]            = TEXT("LatestTime");
const TCHAR c_szComponentType[]         = TEXT("DesktopComponentType");
const TCHAR c_szUsername[]              = TEXT("Username");
const TCHAR c_szPassword[]              = TEXT("Password");
const TCHAR c_szOldIEVersion[]          = TEXT("OldIEVersion");
const TCHAR c_szNonActive[]             = TEXT("NonActive");
const TCHAR c_szOffline[]               = TEXT("Offline");
const TCHAR c_szSynchronize[]           = TEXT("Synchronize");

// Names of reserved schedule groups that we support even in localized version
const WCHAR c_szScheduleAuto[]          = L"Auto";
const WCHAR c_szScheduleDaily[]         = L"Daily";
const WCHAR c_szScheduleWeekly[]        = L"Weekly";
const WCHAR c_szScheduleManual[]        = L"Manual";

// Function prototypes for Modification handlers
HRESULT ProcessAddChannels(HKEY hkey);
HRESULT ProcessRemoveChannels(HKEY hkey);
HRESULT ProcessRemoveAllChannels(HKEY hkey);
HRESULT ProcessAddSubscriptions(HKEY hkey);
HRESULT ProcessRemoveSubscriptions(HKEY hkey);
HRESULT ProcessAddScheduleGroups(HKEY hkey);
HRESULT ProcessRemoveScheduleGroups(HKEY hkey);
HRESULT ProcessAddDesktopComponents(HKEY hkey);
HRESULT ProcessRemoveDesktopComponents(HKEY hkey);

HRESULT Channel_GetBasePath(LPTSTR pszPath, int cch);

// Helper functions
void ShowChannelDirectories(BOOL fShow);

// Table of supported Actions and corresponding functions
// NOTE: The table must be ordered appropriately (RemoveAll must come before Add)
typedef HRESULT (*PFNACTION)(HKEY);
typedef struct { LPCTSTR szAction; PFNACTION pfnAction; } ACTIONTABLE;
ACTIONTABLE rgActionTable[] = {
    { c_szRemoveAllChannels,        &ProcessRemoveAllChannels },
    { c_szRemoveSubscriptions,      &ProcessRemoveSubscriptions },
    { c_szRemoveChannels,           &ProcessRemoveChannels },
    { c_szRemoveDesktopComponents,  &ProcessRemoveDesktopComponents },
    { c_szRemoveScheduleGroups,     &ProcessRemoveScheduleGroups },
    { c_szAddChannels,              &ProcessAddChannels },
    { c_szAddDesktopComponents,     &ProcessAddDesktopComponents },
    { c_szAddScheduleGroups,        &ProcessAddScheduleGroups },
    { c_szAddSubscriptions,         &ProcessAddSubscriptions }
};
#define ACTIONTABLECOUNT (sizeof(rgActionTable) / sizeof(ACTIONTABLE))
#define ACTIONTABLE_ADDCHANNELS 5

// Helper class to manipulate registry keys
class CRegKey
{
    HKEY m_hkey;
    DWORD dwIndex;
public:
    CRegKey(void)
    {
        m_hkey = NULL;
        dwIndex = 0;
    }
    ~CRegKey(void)
    {
        if (m_hkey)
        {
            LONG lRet = RegCloseKey(m_hkey);
            ASSERT(ERROR_SUCCESS == lRet);
            m_hkey = NULL;
        }
    }
    void SetKey(HKEY hkey)
    {
        m_hkey = hkey;
    }
    HKEY GetKey(void)
    {
        return m_hkey;
    }
    HRESULT OpenForRead(HKEY hkey, LPCTSTR szSubKey)
    {
        ASSERT(NULL == m_hkey);
        LONG lRet = RegOpenKeyEx(hkey, szSubKey, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &m_hkey);
        ASSERT((ERROR_SUCCESS == lRet) || !m_hkey);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT CreateForWrite(HKEY hkey, LPCTSTR szSubKey)
    {
        ASSERT(NULL == m_hkey);
        DWORD dwDisp;
        LONG lRet = RegCreateKeyEx(hkey, szSubKey, 0, TEXT(""), 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &m_hkey, &dwDisp);
        ASSERT(ERROR_SUCCESS == lRet);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT GetSubKeyCount(PDWORD pdwKeys)
    {
        ASSERT(NULL != m_hkey);
        LONG lRet = RegQueryInfoKey(m_hkey, NULL, NULL, NULL, pdwKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
        ASSERT(ERROR_SUCCESS == lRet);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT Next(LPTSTR szSubKey)
    {
        ASSERT(NULL != m_hkey);
        DWORD dwLen = MAX_PATH; // Assumes size of incoming buffer.
        LONG lRet = RegEnumKeyEx(m_hkey, dwIndex, szSubKey, &dwLen, NULL, NULL, NULL, NULL);
        dwIndex++;
        if (ERROR_SUCCESS == lRet)
            return S_OK;
        else if (ERROR_NO_MORE_ITEMS == lRet)
            return S_FALSE;
        else
        {
            ASSERT(FALSE);
            return E_FAIL;
        }
    }
    HRESULT Reset(void)
    {
        dwIndex = 0;
        return S_OK;
    }
    HRESULT SetValue(LPCTSTR szValueName, DWORD dwValue)
    {
        ASSERT(m_hkey);
        LONG lRet = RegSetValueEx(m_hkey, szValueName, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(dwValue));
        ASSERT(ERROR_SUCCESS == lRet);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT GetValue(LPCTSTR szValueName, DWORD *pdwValue)
    {
        ASSERT(m_hkey);
        DWORD dwType = REG_DWORD;
        DWORD dwLen = sizeof(DWORD);
        LONG lRet = RegQueryValueEx(m_hkey, szValueName, 0, &dwType, (LPBYTE)pdwValue, &dwLen);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT GetStringValue(LPCTSTR szValueName, LPTSTR szValue, DWORD dwLen)
    {
        ASSERT(m_hkey);
        DWORD dwType = REG_SZ;
        LONG lRet = RegQueryValueEx(m_hkey, szValueName, 0, &dwType, (LPBYTE)szValue, &dwLen);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT SetBSTRValue(LPCTSTR szValueName, BSTR bstr)
    {
        ASSERT(m_hkey);
        TCHAR szValue[INTERNET_MAX_URL_LENGTH];
        MyOleStrToStrN(szValue, ARRAYSIZE(szValue), bstr);
        LONG lRet = RegSetValueEx(m_hkey, szValueName, 0, REG_SZ, (LPBYTE)szValue, lstrlen(szValue) + 1);
        ASSERT(ERROR_SUCCESS == lRet);
        return (ERROR_SUCCESS == lRet)?(S_OK):(E_FAIL);
    }
    HRESULT GetBSTRValue(LPCTSTR szValueName, BSTR *pbstr)
    {
        ASSERT(m_hkey);
        *pbstr = NULL;
        TCHAR szValue[INTERNET_MAX_URL_LENGTH];
        DWORD dwType = REG_SZ;
        DWORD dwLen = sizeof(szValue);
        LONG lRet = RegQueryValueEx(m_hkey, szValueName, 0, &dwType, (LPBYTE)szValue, &dwLen);
        if (ERROR_SUCCESS == lRet)
        {
            *pbstr = SysAllocStringLen(NULL, dwLen);    // dwLen includes null terminator
            if (*pbstr)
            {
                MyStrToOleStrN(*pbstr, dwLen, szValue);
                return S_OK;
            }
        }
        return E_FAIL;
    }
};

// Helper class to manage Dynamic Pointer Arrays of HKEYs.
class CRegKeyDPA
{
    HDPA m_hdpa;
    int m_count;
public:
    CRegKeyDPA(void)
    {
        m_hdpa = NULL;
        m_count = 0;
    }
    ~CRegKeyDPA(void)
    {
        if (m_hdpa)
        {
            ASSERT(m_count);
            int i;
            for (i = 0; i < m_count; i++)
                RegCloseKey(GetKey(i));
            DPA_Destroy(m_hdpa);
        }
    }
    int GetCount(void)
    {
        return m_count;
    }
    HKEY GetKey(int i)
    {
        ASSERT(i >= 0 && i < m_count);
        return (HKEY)DPA_GetPtr(m_hdpa, i);
    }
    HRESULT Add(HKEY hkey, LPCTSTR szSubKey)
    {
        if (!m_hdpa)
        {
            m_hdpa = DPA_CreateEx(5, NULL); // Choose arbitrary growth value
            if (!m_hdpa)
                return E_FAIL;
        }
        HKEY hkeyNew;
        LONG lRet = RegOpenKeyEx(hkey, szSubKey, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkeyNew);
        if (ERROR_SUCCESS != lRet)
            return E_FAIL;
        if (-1 == DPA_InsertPtr(m_hdpa, DPA_APPEND, hkeyNew))
        {
            RegCloseKey(hkeyNew);
            return E_FAIL;
        }
        m_count++;
        return S_OK;
    }
};

//
// 8/18/98 darrenmi
// Copied (and butchered) from shdocvw\util.cpp so we don't have to load it at startup
//
DWORD WCRestricted2W(BROWSER_RESTRICTIONS rest, LPCWSTR pwzUrl, DWORD dwReserved)
{
    DWORD dwType, dw = 0, dwSize = sizeof(DWORD);

    // we only handle NoChannelUI restriction
    if(rest != REST_NoChannelUI)
    {
        return 0;
    }

    // read registry setting
    SHGetValue(HKEY_CURRENT_USER,
            TEXT("Software\\Policies\\Microsoft\\Internet Explorer\\Infodelivery\\Restrictions"),
            TEXT("NoChannelUI"),
            &dwType, &dw, &dwSize);

    return dw;
}


// ProcessInfodeliveryPolicies
//
// This is the main Admin API for Infodelivery.  It returns E_FAIL for errors,
// S_FALSE for nothing to process, and S_OK for correctly processed items.
//
// Reg key organization     [Modifications]         - the key to process
//                              [GUID1]             - group of actions
//                                  [AddChannels]   - sample action
//                                      [Channel1]  - element of an action
//
HRESULT ProcessInfodeliveryPolicies(void)
{
    HRESULT hr;
    CRegKey regModifications;
    TCHAR   szGUID[MAX_PATH];

    // Check if channels should be hidden.
    if (WCRestricted2W(REST_NoChannelUI, NULL, 0))
    {
        ShowChannelDirectories(FALSE);
    }
    else
    {
        ShowChannelDirectories(TRUE);
    }
    
    // Bail out quickly if there are no Modifications to perform. (Return S_FALSE)
    hr = regModifications.OpenForRead(HKEY_CURRENT_USER, c_szRegKeyModifications);
    if (FAILED(hr))
        return S_FALSE;

    // Prepare to use the CompletedModifications key.
    CRegKey regCompletedMods;
    hr = regCompletedMods.CreateForWrite(HKEY_CURRENT_USER, c_szRegKeyCompletedMods);
    if (FAILED(hr))
        return hr;

    hr = CoInitialize(NULL);
    if (FAILED(hr))
        return hr;

    // Prepare queues of registry keys to actions
    CRegKeyDPA rgKeyQueue[ACTIONTABLECOUNT];

    // Enumerate the GUID keys, skipping the completed ones.
    // Enumerate the Actions beneath them and add them to queues.
    // ignoring errors here too.
    while (S_OK == regModifications.Next(szGUID))
    {
        DWORD dwValue;
        if (FAILED(regCompletedMods.GetValue(szGUID, &dwValue)))
        {
            CRegKey regGUID;
            TCHAR   szAction[MAX_PATH];
            hr = regGUID.OpenForRead(regModifications.GetKey(), szGUID);
            while (S_OK == regGUID.Next(szAction))
            {
                // Search the table to see if it's a key we understand.
                // If so, add it to the queue.
                int i;
                for (i = 0; i < ACTIONTABLECOUNT; i++)
                {
                    if (!StrCmpI(rgActionTable[i].szAction, szAction))
                    {
                        rgKeyQueue[i].Add(regGUID.GetKey(), szAction);
                        break;
                    }
                }
            }
        }
    }

    // Process all the keys we've accumulated.  (Correct order is assumed.)
    int i;
    for (i = 0; i < ACTIONTABLECOUNT; i++)
    {
        if (rgKeyQueue[i].GetCount())
        {
            int iKey;
            for (iKey = 0; iKey < rgKeyQueue[i].GetCount(); iKey++)
            {
                (rgActionTable[i].pfnAction)(rgKeyQueue[i].GetKey(iKey));
            }
        }
    }

    // Walk the GUIDs we've processed and mark them completed with the time.
    // Updating ones we skipped as well will help with garbage collection.
    regModifications.Reset();
    while (S_OK == regModifications.Next(szGUID))
    {
        SYSTEMTIME st;
        FILETIME ft;
        GetSystemTime(&st);
        SystemTimeToFileTime(&st, &ft);
        regCompletedMods.SetValue(szGUID, ft.dwHighDateTime);
    }

    // Delete the Actions.  NOTE: NT's RegDeleteKey() doesn't delete sub-keys.
    // This shlwapi API uses KEY_ALL_ACCESS.
    // We probably have to close all the keys here.
    SHDeleteKey(HKEY_CURRENT_USER, c_szRegKeyModifications);

    // If any channels were processed, tell the cache to reload.
    // We should only do this for default channels.
    if (rgKeyQueue[ACTIONTABLE_ADDCHANNELS].GetCount())
    {
        ASSERT(!StrCmpI(rgActionTable[ACTIONTABLE_ADDCHANNELS].szAction, c_szAddChannels));
        LoadUrlCacheContent();
    }

    CoUninitialize();

    return S_OK;
}

//
// ProcessAddChannels_SortCallback - sort in reverse order
//
int ProcessAddChannels_SortCallback(PVOID p1, PVOID p2, LPARAM lparam)
{
    return StrCmpI((LPTSTR)p2, (LPTSTR)p1);
}

//
// ProcessAddChannels
//
HRESULT ProcessAddChannels(HKEY hkey)
{
    // Enumerate the channels in the AddChannels key
    HRESULT hr;
    DWORD dwChannels;
    CRegKey regAdd;
    regAdd.SetKey(hkey);
    hr = regAdd.GetSubKeyCount(&dwChannels);
    if (SUCCEEDED(hr) && dwChannels)
    {
        // Check if the channels are the same code page as the system default.
        BOOL bCodePageMatch = TRUE;
        LCID lcidChannel = 0;
        if (SUCCEEDED(regAdd.GetValue(c_szLCID, &lcidChannel)))
        {
            TCHAR szCodePageSystem[8];
            TCHAR szCodePageChannel[8];
            szCodePageChannel[0] = 0;   // Init in case there's no locale info
            GetLocaleInfo(lcidChannel, LOCALE_IDEFAULTANSICODEPAGE, szCodePageChannel, ARRAYSIZE(szCodePageChannel));
            int iRet = GetLocaleInfo(GetSystemDefaultLCID(), LOCALE_IDEFAULTANSICODEPAGE, szCodePageSystem, ARRAYSIZE(szCodePageSystem));
            ASSERT(iRet);
            if (StrCmpI(szCodePageSystem, szCodePageChannel))
                bCodePageMatch = FALSE;
        }
    
        hr = E_FAIL;
        TCHAR *pch = (TCHAR *)MemAlloc(LMEM_FIXED, dwChannels * MAX_PATH * sizeof(TCHAR));
        if (pch)
        {
            HDPA hdpa = DPA_Create(dwChannels);
            if (hdpa)
            {
                DWORD i;
                TCHAR *pchCur = pch;
                for (i = 0; i < dwChannels; i++)
                {
                    if ((S_OK != regAdd.Next(pchCur)) || (-1 == DPA_InsertPtr(hdpa, DPA_APPEND, pchCur)))
                        break;
                    pchCur += MAX_PATH;
                }
                if (i >= dwChannels)
                {
                    // Sort channels by registry key name,
                    DPA_Sort(hdpa, ProcessAddChannels_SortCallback, 0);
                    // Now create them.
                    for (i = 0; i < dwChannels; i++)
                    {
                        BSTR bstrURL = NULL;
                        BSTR bstrTitle = NULL;
                        BSTR bstrLogo = NULL;
                        BSTR bstrWideLogo = NULL;
                        BSTR bstrIcon = NULL;
                        BSTR bstrPreloadURL = NULL;
                        DWORD dwCategory = 0;       // default to channel
                        DWORD dwChannelGuide = 0;   // default to not a guide
                        DWORD dwSoftware = 0;       // default to non-software channel
                        DWORD dwOffline = 0;
                        DWORD dwSynchronize = 0;
                        CRegKey regChannel;
                        regChannel.OpenForRead(hkey, (LPCTSTR)DPA_GetPtr(hdpa, i));
                        hr = regChannel.GetBSTRValue(c_szURL, &bstrURL);
                        hr = regChannel.GetBSTRValue(c_szTitle, &bstrTitle);
                        hr = regChannel.GetBSTRValue(c_szLogo, &bstrLogo);
                        hr = regChannel.GetBSTRValue(c_szWideLogo, &bstrWideLogo);
                        hr = regChannel.GetBSTRValue(c_szIcon, &bstrIcon);
                        hr = regChannel.GetBSTRValue(c_szPreloadURL, &bstrPreloadURL);
                        hr = regChannel.GetValue(c_szCategory, &dwCategory);
                        hr = regChannel.GetValue(c_szChannelGuide, &dwChannelGuide);
                        hr = regChannel.GetValue(c_szSoftware, &dwSoftware);
                        hr = regChannel.GetValue(c_szOffline, &dwOffline);
                        hr = regChannel.GetValue(c_szSynchronize, &dwSynchronize);
                        if (bstrTitle)
                        {
                            IChannelMgr *pChannelMgr = NULL;
                            hr = CoCreateInstance(CLSID_ChannelMgr, NULL, CLSCTX_INPROC_SERVER, IID_IChannelMgr, (void**)&pChannelMgr);
                            if (SUCCEEDED(hr))
                            {
                                // See if channel already exists - do nothing if it does (62976)
                                IEnumChannels *pEnumChannels = NULL;
                                if (SUCCEEDED(pChannelMgr->EnumChannels(CHANENUM_ALLFOLDERS, bstrURL, &pEnumChannels)))
                                {
                                    CHANNELENUMINFO Bogus={0};
                                    ULONG cFetched=0;

                                    if ((S_OK == pEnumChannels->Next(1, &Bogus, &cFetched)) && cFetched)
                                    {
                                        // Oops. It exists. Skip all this goo.
                                        hr = E_FAIL;
                                    }
                                }
                                SAFERELEASE(pEnumChannels);
                            }
                            if (SUCCEEDED(hr))
                            {
                                if (dwCategory && bCodePageMatch)
                                {
                                    // create a category (useless if code page doesn't match)
                                    CHANNELCATEGORYINFO csi = {0};
                                    csi.cbSize   = sizeof(csi);
                                    csi.pszURL   = bstrURL;
                                    csi.pszTitle = bstrTitle;
                                    csi.pszLogo  = bstrLogo;
                                    csi.pszIcon  = bstrIcon;
                                    csi.pszWideLogo = bstrWideLogo;
                                    hr = pChannelMgr->AddCategory(&csi);
                                }
                                else if (!dwCategory && bstrURL)
                                {
                                    // update the registry if it's a channel guide
                                    if (dwChannelGuide)
                                    {
                                        CRegKey reg;
                                        hr = reg.CreateForWrite(HKEY_CURRENT_USER, c_szRegKey);
                                        if (SUCCEEDED(hr))
                                            reg.SetBSTRValue(c_szChannelGuide, bstrTitle);
                                    }
                                    // tell wininet if there's preload content
                                    if (bstrPreloadURL)
                                    {
                                        CRegKey reg;
                                        hr = reg.CreateForWrite(HKEY_CURRENT_USER, c_szRegKeyCachePreload);
                                        if (SUCCEEDED(hr))
                                        {
                                            TCHAR szURL[INTERNET_MAX_URL_LENGTH];
                                            MyOleStrToStrN(szURL, ARRAYSIZE(szURL), bstrURL);
                                            reg.SetBSTRValue(szURL, bstrPreloadURL);
                                        }
                                    }
                                    // create a channel (use URL instead of Title if code page doesn't match)
                                    CHANNELSHORTCUTINFO csi = {0};
                                    csi.cbSize   = sizeof(csi);
                                    csi.pszURL   = bstrURL;
                                    if (bCodePageMatch)
                                        csi.pszTitle = bstrTitle;
                                    else
                                        csi.pszTitle = bstrURL;
                                    csi.pszLogo  = bstrLogo;
                                    csi.pszIcon  = bstrIcon;
                                    csi.pszWideLogo = bstrWideLogo;
                                    if (dwSoftware)
                                        csi.bIsSoftware = TRUE;
                                    hr = pChannelMgr->AddChannelShortcut(&csi);
                                }
                            }
                            SAFERELEASE(pChannelMgr);

                            if (dwOffline)
                            {
                                ISubscriptionMgr2 *pSubMgr2 = NULL;
                                hr = CoCreateInstance(CLSID_SubscriptionMgr, 
                                                      NULL, 
                                                      CLSCTX_INPROC_SERVER, 
                                                      IID_ISubscriptionMgr2, 
                                                      (void**)&pSubMgr2);
                                if (SUCCEEDED(hr))
                                {
                                    hr = pSubMgr2->CreateSubscription(NULL, 
                                                                      bstrURL, 
                                                                      bstrTitle, 
                                                                      CREATESUBS_NOUI,
                                                                      SUBSTYPE_CHANNEL, 
                                                                      NULL);

                                    if (dwSynchronize)
                                    {
                                        BOOL bIsSubscribed;
                                        SUBSCRIPTIONCOOKIE cookie;

                                        if (SUCCEEDED(pSubMgr2->IsSubscribed(bstrURL, &bIsSubscribed))
                                            && bIsSubscribed &&
                                            SUCCEEDED(ReadCookieFromInetDB(bstrURL, &cookie)))
                                        {
                                            pSubMgr2->UpdateItems(SUBSMGRUPDATE_MINIMIZE, 1, &cookie);
                                        }
                                    }
                                    pSubMgr2->Release();
                                }
                            }
                        }
                        SAFEFREEBSTR(bstrURL);
                        SAFEFREEBSTR(bstrTitle);
                        SAFEFREEBSTR(bstrLogo);
                        SAFEFREEBSTR(bstrWideLogo);
                        SAFEFREEBSTR(bstrIcon);
                        SAFEFREEBSTR(bstrPreloadURL);
                    }
                }
                DPA_Destroy(hdpa);
            }
            MemFree(pch);
        }
    }
    regAdd.SetKey(NULL);
    return S_OK;
}

//
// ProcessRemoveChannels
//
HRESULT ProcessRemoveChannels(HKEY hkey)
{
    // Enumerate the channel keys in the RemoveChannels key
    HRESULT hr;
    CRegKey reg;
    reg.SetKey(hkey);
    TCHAR szChannel[MAX_PATH];
    while (S_OK == reg.Next(szChannel))
    {
        CRegKey regChannel;
        DWORD dwNonActive = 0;  // default to deleting Active & NonActive channels
        TCHAR szURL[INTERNET_MAX_URL_LENGTH];
        regChannel.OpenForRead(hkey, szChannel);
        regChannel.GetValue(c_szNonActive, &dwNonActive);
        if (SUCCEEDED(regChannel.GetStringValue(c_szURL, szURL, sizeof(szURL))))
        {
            // Check if the channel is Active to determine if we can delete it
            if (dwNonActive)
            {
                CRegKey regPreload;
                if (SUCCEEDED(regPreload.OpenForRead(HKEY_CURRENT_USER, c_szRegKeyCachePreload)))
                {
                    if (SUCCEEDED(regPreload.GetStringValue(szURL, NULL, 0)))
                    {
                        dwNonActive = 0;
                    }
                }
            }

            // Now delete the channel if appropriate
            if (!dwNonActive)
            {
                IChannelMgr *pChannelMgr = NULL;
                hr = CoCreateInstance(CLSID_ChannelMgr, NULL, CLSCTX_INPROC_SERVER, IID_IChannelMgr, (void**)&pChannelMgr);
                if (SUCCEEDED(hr))
                {
                    BSTR bstrURL;
                    if (SUCCEEDED(regChannel.GetBSTRValue(c_szURL, &bstrURL)))
                    {
                        IEnumChannels *pEnum;
                        hr = pChannelMgr->EnumChannels(CHANENUM_ALLFOLDERS | CHANENUM_PATH, bstrURL, &pEnum);
                        if (SUCCEEDED(hr))
                        {
                            CHANNELENUMINFO info;
                            while (S_OK == pEnum->Next(1, &info, NULL))
                            {
                                hr = pChannelMgr->DeleteChannelShortcut(info.pszPath);
                                ASSERT(SUCCEEDED(hr));
                                CoTaskMemFree(info.pszPath);
                            }
                            pEnum->Release();
                        }
                        SysFreeString(bstrURL);
                    }
                    pChannelMgr->Release();
                }
            }    
        }
    }
    reg.SetKey(NULL);
    return S_OK;
}

//
// IsScheduleGroupReserved
//
HRESULT IsScheduleGroupReserved(LPCWSTR wzName, SUBSCRIPTIONSCHEDULE *pSched)
{
    HRESULT hr = S_OK;
    if (!StrCmpIW(wzName, c_szScheduleAuto))
        *pSched = SUBSSCHED_AUTO;
    else if (!StrCmpIW(wzName, c_szScheduleDaily))
        *pSched = SUBSSCHED_DAILY;
    else if (!StrCmpIW(wzName, c_szScheduleWeekly))
        *pSched = SUBSSCHED_WEEKLY;
    else if (!StrCmpIW(wzName, c_szScheduleManual))
        *pSched = SUBSSCHED_MANUAL;
    else
    {
        *pSched = SUBSSCHED_CUSTOM;
        hr = E_FAIL;
    }
    return hr;
}

//
// FindScheduleGroupFromName
//
HRESULT FindScheduleGroupFromName(LPCWSTR wzName, PNOTIFICATIONCOOKIE pCookie)
{
    // FEATURE: Need to worry about localized names.
    HRESULT hrRet = E_FAIL;
    HRESULT hr;
    INotificationMgr *pNotMgr = NULL;
    hr = CoCreateInstance(CLSID_StdNotificationMgr, NULL, CLSCTX_INPROC_SERVER, IID_INotificationMgr, (void**)&pNotMgr);
    if (SUCCEEDED(hr))
    {
        // what flags?
        IEnumScheduleGroup *pEnumScheduleGroup = NULL;
        hr = pNotMgr->GetEnumScheduleGroup(0, &pEnumScheduleGroup);
        if (SUCCEEDED(hr))
        {
            // Iterate through schedule groups
            for (;;)
            {
                IScheduleGroup *pScheduleGroup = NULL;
                ULONG uFetched;
                hr = pEnumScheduleGroup->Next(1, &pScheduleGroup, &uFetched);
                if (SUCCEEDED(hr) && (1 == uFetched))
                {
                    GROUPINFO info = { 0 };
                    hr = pScheduleGroup->GetAttributes(NULL, NULL, pCookie, &info, NULL, NULL);
                    if (SUCCEEDED(hr))
                    {
                        ASSERT(info.cbSize == SIZEOF(GROUPINFO));
                        ASSERT(info.pwzGroupname != NULL);
                        if (!StrCmpW(info.pwzGroupname, wzName))
                        {
                            // found it
                            hrRet = S_OK;
                            SAFEDELETE(info.pwzGroupname);
                            break;
                        }
                        SAFEDELETE(info.pwzGroupname);
                    }
                    pScheduleGroup->Release();
                }
                else
                {
                    break;
                }
            }
            pEnumScheduleGroup->Release();
        }
        pNotMgr->Release();
    }
    return hrRet;
}

//
// ProcessAddSubscriptions
//
HRESULT ProcessAddSubscriptions(HKEY hkey)
{
    // Enumerate the subscription keys in the AddSubscriptions key
    HRESULT hr;
    CRegKey reg;
    reg.SetKey(hkey);
    TCHAR szSubscription[MAX_PATH];
    while (S_OK == reg.Next(szSubscription))
    {
        // Create the subscription
        // What if there is one already?
        CRegKey regSubscription;
        regSubscription.OpenForRead(hkey, szSubscription);
        BSTR bstrURL, bstrTitle, bstrGroup, bstrUsername, bstrPassword;
        DWORD dwSubType;
        DWORD dwSynchronize = 0;
        hr = regSubscription.GetBSTRValue(c_szURL, &bstrURL);
        hr = regSubscription.GetBSTRValue(c_szTitle, &bstrTitle);
        hr = regSubscription.GetBSTRValue(c_szScheduleGroup, &bstrGroup);
        hr = regSubscription.GetBSTRValue(c_szUsername, &bstrUsername);
        hr = regSubscription.GetBSTRValue(c_szPassword, &bstrPassword);
        hr = regSubscription.GetValue(c_szSynchronize, &dwSynchronize);
        if (bstrURL && bstrTitle && bstrGroup && SUCCEEDED(regSubscription.GetValue(c_szSubscriptionType, &dwSubType)))
        {
            SUBSCRIPTIONINFO si = {0};
            si.cbSize = sizeof(SUBSCRIPTIONINFO);
            si.fUpdateFlags = SUBSINFO_SCHEDULE;
            if (bstrUsername && bstrPassword)
            {
                si.fUpdateFlags |= (SUBSINFO_USER | SUBSINFO_PASSWORD);
                si.bstrUserName = bstrUsername;
                si.bstrPassword = bstrPassword;
            }
            if (dwSubType == SUBSTYPE_CHANNEL || dwSubType == SUBSTYPE_DESKTOPCHANNEL)
            {
                si.fUpdateFlags |= SUBSINFO_CHANNELFLAGS;
                si.fChannelFlags = 0;   //  Notify only.
            }
            hr = IsScheduleGroupReserved(bstrGroup, &si.schedule);
            if (FAILED(hr))
            {
                hr = FindScheduleGroupFromName(bstrGroup, &si.customGroupCookie);
            }

            if (SUCCEEDED(hr))
            {
                ISubscriptionMgr2 *pSubMgr2 = NULL;
                hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr2, (void**)&pSubMgr2);
                if (SUCCEEDED(hr))
                {
                    hr = pSubMgr2->CreateSubscription(NULL, bstrURL, bstrTitle, CREATESUBS_NOUI,
                                                     (SUBSCRIPTIONTYPE)dwSubType, &si);
                    if (dwSynchronize)
                    {
                        BOOL bIsSubscribed;
                        SUBSCRIPTIONCOOKIE cookie;

                        if (SUCCEEDED(pSubMgr2->IsSubscribed(bstrURL, &bIsSubscribed)) && 
                            bIsSubscribed &&
                            SUCCEEDED(ReadCookieFromInetDB(bstrURL, &cookie)))
                        {
                            pSubMgr2->UpdateItems(SUBSMGRUPDATE_MINIMIZE, 1, &cookie);
                        }
                    }

                    pSubMgr2->Release();
                }
            }
        }
        SAFEFREEBSTR(bstrURL);
        SAFEFREEBSTR(bstrTitle);
        SAFEFREEBSTR(bstrGroup);
        SAFEFREEBSTR(bstrUsername);
        SAFEFREEBSTR(bstrPassword);
    }
    reg.SetKey(NULL);
    return S_OK;
}

//
// ProcessRemoveSubscriptions
//
HRESULT ProcessRemoveSubscriptions(HKEY hkey)
{
    // Enumerate the subscription keys in the RemoveSubscriptions key
    HRESULT hr;
    CRegKey reg;
    reg.SetKey(hkey);
    TCHAR szSubscription[MAX_PATH];
    while (S_OK == reg.Next(szSubscription))
    {
        // Find the URL to delete
        CRegKey regSubscription;
        regSubscription.OpenForRead(hkey, szSubscription);
        BSTR bstrURL;
        if (SUCCEEDED(regSubscription.GetBSTRValue(c_szURL, &bstrURL)))
        {
            ISubscriptionMgr *pSubMgr = NULL;
            hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER, IID_ISubscriptionMgr, (void**)&pSubMgr);
            if (SUCCEEDED(hr))
            {
                hr = pSubMgr->DeleteSubscription(bstrURL, NULL);
                pSubMgr->Release();
            }
            SysFreeString(bstrURL);
        }
    }
    reg.SetKey(NULL);
    return S_OK;
}

//
// PRIVATE VERSION HANDLING CODE - REVIEW THIS CODE SHOULD HAVE BEEN STOLEN 
// FROM SETUP
//
struct MYVERSION
{
    DWORD dw1;  // most sig version number
    DWORD dw2;
    DWORD dw3;
    DWORD dw4;  // least sig version number
};

int CompareDW(DWORD dw1, DWORD dw2)
{
    if (dw1 > dw2)
        return 1;
    if (dw1 < dw2)
        return -1;

    return 0;
}

int CompareVersion(MYVERSION * pv1, MYVERSION * pv2)
{
    int rv;

    rv = CompareDW(pv1->dw1, pv2->dw1);

    if (rv == 0)
    {
        rv = CompareDW(pv1->dw2, pv2->dw2);

        if (rv == 0)
        {
            rv = CompareDW(pv1->dw3, pv2->dw3);

            if (rv == 0)
            {
                rv = CompareDW(pv1->dw4, pv2->dw4);
            }
        }
    }

    return rv;
}

//
// Returns TRUE if an INT was parsed and *pwsz is NOT NULL
// if a . was found
//
BOOL GetDWORDFromStringAndAdvancePtr(DWORD *pdw, LPWSTR *pwsz)
{
    if (!StrToIntExW(*pwsz, 0, (int *)pdw))
        return FALSE;

    *pwsz = StrChrW(*pwsz, L'.');

    if (*pwsz)
        *pwsz = *pwsz +1;

    return TRUE;
}

BOOL GetVersionFromString(MYVERSION *pver, LPWSTR pwsz)
{
    BOOL rv;

    rv = GetDWORDFromStringAndAdvancePtr(&pver->dw1, &pwsz);
    if (!rv || pwsz == NULL)
        return FALSE;

    rv = GetDWORDFromStringAndAdvancePtr(&pver->dw2, &pwsz);
    if (!rv || pwsz == NULL)
        return FALSE;

    rv = GetDWORDFromStringAndAdvancePtr(&pver->dw3, &pwsz);
    if (!rv || pwsz == NULL)
        return FALSE;

    rv = GetDWORDFromStringAndAdvancePtr(&pver->dw4, &pwsz);
    if (!rv)
        return FALSE;

    return TRUE;
}

//
// ProcessRemoveAllChannels
//
HRESULT ProcessRemoveAllChannels(HKEY hkey)
{
    HRESULT hr;
    HINSTANCE hAdvPack = NULL;
    DELNODE pfDELNODE = NULL;
    IChannelMgrPriv *pChannelMgrPriv = NULL;
    CRegKey regAdd;
    regAdd.SetKey(hkey);
    TCHAR szChannelFolder[MAX_PATH];

    hr = CoCreateInstance(CLSID_ChannelMgr, NULL, CLSCTX_INPROC_SERVER, IID_IChannelMgrPriv, (void**)&pChannelMgrPriv);
    if (FAILED(hr))
    {
        goto Exit;
    }

    if ((hAdvPack = LoadLibrary(TEXT("advpack.dll"))) != NULL) 
    {
        pfDELNODE = (DELNODE)GetProcAddress( hAdvPack, "DelNode");
        if (!pfDELNODE) 
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
            goto Exit;
        }
    }
    else
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
        goto Exit;
    }

    // Loop Through Channel Folders to delete
    while (S_OK == regAdd.Next(szChannelFolder))
    {
        DWORD dwSoftware = 0, dwChannelGuide = 0;
        CRegKey regChannelFolder;
 
        CHAR szChannelPath[MAX_PATH];
        TCHAR szChannelPathT[MAX_PATH];
        TCHAR szFavsT[MAX_PATH]; //Retrieve Unicode data from registry

        BSTR bstrOldIEVersion = NULL;
        BOOL bVersion = TRUE;
        regChannelFolder.OpenForRead(hkey, szChannelFolder);
        
        // Check whether old IE version is correct.
        hr = regChannelFolder.GetBSTRValue(c_szOldIEVersion, &bstrOldIEVersion);
        if (SUCCEEDED(hr) && bstrOldIEVersion)
        {
            CRegKey regKeyIESetup;
            hr = regKeyIESetup.OpenForRead(HKEY_LOCAL_MACHINE, c_szRegKeyIESetup);
    
            if (SUCCEEDED(hr))
            {
                BSTR bstrRealOldIEVersion = NULL;
                hr = regKeyIESetup.GetBSTRValue(c_szOldIEVersion, &bstrRealOldIEVersion);
                if (SUCCEEDED(hr) && bstrRealOldIEVersion)
                {
                    MYVERSION verOldIEVersion, verRealOldIEVersion;

                    if (GetVersionFromString(&verOldIEVersion,     bstrOldIEVersion) &&
                        GetVersionFromString(&verRealOldIEVersion, bstrRealOldIEVersion))
                    {
                        //
                        // If the old version of IE that was on this machine (verRealOldIEVersion)
                        // is infact NEWER than the old version number in the CABs that we want to 
                        // delete (verOldIEVersion) then dont blow away old channel folder.
                        // Otherwise default to blow away channels.
                        //
                        if (CompareVersion(&verRealOldIEVersion, &verOldIEVersion) > 0)
                        {
                            bVersion = FALSE;
                        }
                    }

                    SAFEFREEBSTR(bstrRealOldIEVersion);
                }
            }
            SAFEFREEBSTR(bstrOldIEVersion);
        }

        if (!bVersion)
        {
            continue;
        }
        
        hr = regChannelFolder.GetValue(c_szChannelGuide, &dwChannelGuide);
        if (FAILED(hr) || (SUCCEEDED(hr) && !dwChannelGuide))
        {
            if (SUCCEEDED(pChannelMgrPriv->GetChannelFolderPath(szChannelPath, MAX_PATH, IChannelMgrPriv::CF_CHANNEL)))
            {
                // Retrieve Favorites Path from registry
                if (SUCCEEDED(Channel_GetBasePath((LPTSTR)szFavsT, ARRAYSIZE(szFavsT))))
                {   
                    // Convert from ANSI
                    SHAnsiToTChar(szChannelPath, szChannelPathT, ARRAYSIZE(szChannelPathT));
                    // If channel folder doesn't exist, then szChannelPath will contain the Favorites path.
                    // Don't delete the entries.
                    if (StrCmpI(szFavsT, szChannelPathT))
                       pfDELNODE(szChannelPath, ADN_DONT_DEL_DIR);
                }
            }
        }

        hr = regChannelFolder.GetValue(c_szSoftware, &dwSoftware);
        if (FAILED(hr) || (SUCCEEDED(hr) && !dwSoftware))
        {
            if (SUCCEEDED(pChannelMgrPriv->GetChannelFolderPath(szChannelPath, MAX_PATH, IChannelMgrPriv::CF_SOFTWAREUPDATE)))
            {
                pfDELNODE(szChannelPath, ADN_DONT_DEL_DIR);
            }
        }

        hr = S_OK;                        
    }
    regAdd.SetKey(NULL);

Exit:
    SAFERELEASE(pChannelMgrPriv);
    
    if (hAdvPack) {
        FreeLibrary(hAdvPack);
    }

    return hr;
}

//
// ProcessAddScheduleGroups
//
HRESULT ProcessAddScheduleGroups(HKEY hkey)
{
    // Enumerate the schedule group keys in the AddScheduleGroups key
    HRESULT hr;
    CRegKey reg;
    reg.SetKey(hkey);
    TCHAR szScheduleGroup[MAX_PATH];
    while (S_OK == reg.Next(szScheduleGroup))
    {
        // Read the Title and the Earliest, Latest, and Interval times
        // Currently times must be in minutes.
        // FEATURE: Currently we don't look for StartDate or EndDate.
        CRegKey regScheduleGroup;
        regScheduleGroup.OpenForRead(hkey, szScheduleGroup);
        BSTR bstrTitle;
        DWORD dwET, dwIT, dwLT;
        hr = regScheduleGroup.GetBSTRValue(c_szTitle, &bstrTitle);
        if (SUCCEEDED(hr)
            && SUCCEEDED(regScheduleGroup.GetValue(c_szEarliestTime, &dwET))
            && SUCCEEDED(regScheduleGroup.GetValue(c_szIntervalTime, &dwIT))
            && SUCCEEDED(regScheduleGroup.GetValue(c_szLatestTime, &dwLT)))
        {
            INotificationMgr *pNotMgr = NULL;
            hr = CoCreateInstance(CLSID_StdNotificationMgr, NULL, CLSCTX_INPROC_SERVER, IID_INotificationMgr, (void**)&pNotMgr);
            if (SUCCEEDED(hr))
            {
                // Create the schedule group
                IScheduleGroup *pScheduleGroup = NULL;
                NOTIFICATIONCOOKIE cookie;
                hr = pNotMgr->CreateScheduleGroup(0, &pScheduleGroup, &cookie, 0);
                if (SUCCEEDED(hr))
                {
                    TASK_TRIGGER tt;
                    GROUPINFO gi = { 0 };
                    SYSTEMTIME st;
                    GetLocalTime(&st);
                    tt.cbTriggerSize = sizeof(tt);
                    if (SUCCEEDED(ScheduleToTaskTrigger(&tt, &st, NULL, (long)dwIT, (long)dwET, (long)dwLT)))
                    {
                        gi.cbSize = sizeof(GROUPINFO);
                        gi.pwzGroupname = bstrTitle;
                    
                        hr = pScheduleGroup->SetAttributes(&tt, NULL, &cookie, &gi, 0);
                    }
                    pScheduleGroup->Release();
                }
                pNotMgr->Release();
            }
        }
        SAFEFREEBSTR(bstrTitle);
    }
    reg.SetKey(NULL);
    return S_OK;
}

//
// ProcessRemoveScheduleGroups
//
HRESULT ProcessRemoveScheduleGroups(HKEY hkey)
{
    // Enumerate the schedule group keys in the RemoveScheduleGroups key
    HRESULT hr;
    CRegKey reg;
    reg.SetKey(hkey);
    TCHAR szScheduleGroup[MAX_PATH];
    while (S_OK == reg.Next(szScheduleGroup))
    {
        // Find the title to delete
        CRegKey regScheduleGroup;
        regScheduleGroup.OpenForRead(hkey, szScheduleGroup);
        BSTR bstrTitle;
        if (SUCCEEDED(regScheduleGroup.GetBSTRValue(c_szTitle, &bstrTitle)))
        {
            GUID groupCookie;
            if (SUCCEEDED(FindScheduleGroupFromName(bstrTitle, &groupCookie)))
            {
                INotificationMgr *pNotMgr = NULL;
                hr = CoCreateInstance(CLSID_StdNotificationMgr, NULL, CLSCTX_INPROC_SERVER, IID_INotificationMgr, (void**)&pNotMgr);
                if (SUCCEEDED(hr))
                {
                    hr = pNotMgr->RevokeScheduleGroup(&groupCookie, NULL, 0);
                    pNotMgr->Release();
                }
            }
            SysFreeString(bstrTitle);
        }
    }
    reg.SetKey(NULL);
    return S_OK;
}

//
// ProcessAddDesktopComponents
//
HRESULT ProcessAddDesktopComponents(HKEY hkey)
{
    return S_OK;
}

//
// ProcessRemoveDesktopComponents
//
HRESULT ProcessRemoveDesktopComponents(HKEY hkey)
{
    // Enumerate the component keys in the ProcessRemoveDesktopComponents key
    // HRESULT hr;
    CRegKey reg;
    reg.SetKey(hkey);
    TCHAR szComponent[MAX_PATH];
    while (S_OK == reg.Next(szComponent))
    {
        // Find the URL to delete
        CRegKey regComponent;
        regComponent.OpenForRead(hkey, szComponent);
        BSTR bstrURL;
        if (SUCCEEDED(regComponent.GetBSTRValue(c_szURL, &bstrURL)))
        {
            SysFreeString(bstrURL);
        }
    }
    reg.SetKey(NULL);
    return S_OK;
}


//
// NoChannelUI processing.
//

#define SHELLFOLDERS \
   TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders")


typedef enum _tagXMLDOCTYPE {
    DOC_CHANNEL,
    DOC_SOFTWAREUPDATE
} XMLDOCTYPE;

//
// Get the path to the favorites directory.
//
HRESULT Channel_GetBasePath(LPTSTR pszPath, int cch)
{
    ASSERT(pszPath || 0 == cch);

    HRESULT hr = E_FAIL;

    HKEY hKey;
    DWORD dwLen = cch;

    if (RegOpenKey(HKEY_CURRENT_USER, SHELLFOLDERS, &hKey) == ERROR_SUCCESS)
    {
        if (RegQueryValueEx(hKey, TEXT("Favorites"), NULL, NULL,
                            (LPBYTE)pszPath, &dwLen) == ERROR_SUCCESS)
        {
            hr = S_OK;
        }
        RegCloseKey(hKey);
    }
    
    return hr;
}

HRESULT Channel_GetFolder(LPTSTR pszPath, XMLDOCTYPE xdt )
{
    TCHAR   szFavs[MAX_PATH];
    TCHAR   szChannel[MAX_PATH];
    HRESULT hr = E_FAIL;

    if (SUCCEEDED(Channel_GetBasePath(szFavs, ARRAYSIZE(szFavs))))
    {
        //
        // Get the potentially localized name of the Channel folder from
        // tack this on the Favorites path 
        //
        MLLoadString(
                   ((xdt == DOC_CHANNEL)? IDS_CHANNEL_FOLDER : IDS_SOFTWAREUPDATE_FOLDER),
                   szChannel, MAX_PATH);
        PathCombine(pszPath, szFavs, szChannel);

        hr = S_OK;
    }
    return hr;
}

//
// Set/Clear the "hidden" attribute of a channel directory.
//

void ShowChannelDirectory(BOOL fShow, XMLDOCTYPE xdt)
{
    TCHAR szPath[MAX_PATH];
    DWORD dwAttributes;

    if (SUCCEEDED(Channel_GetFolder(szPath, xdt)))
    {
        dwAttributes = GetFileAttributes(szPath);
        
        if (0xffffffff != dwAttributes)
        {
            if (fShow && (dwAttributes & FILE_ATTRIBUTE_HIDDEN))
            {
                SetFileAttributes(szPath, dwAttributes & ~FILE_ATTRIBUTE_HIDDEN);
            }
            else if (!fShow && !(dwAttributes & FILE_ATTRIBUTE_HIDDEN))
            {
                SetFileAttributes(szPath, dwAttributes | FILE_ATTRIBUTE_HIDDEN);
            }
        }
    }

    return;
}

//
// Hide or show channel directories
//

void ShowChannelDirectories(BOOL fShow)
{
    ShowChannelDirectory(fShow, DOC_CHANNEL);
    ShowChannelDirectory(fShow, DOC_SOFTWAREUPDATE);

    return;
}