#include "priv.h"
#include "caggunk.h"
#include "srchasst.h"
#include "dhuihand.h"
#include "mlang.h"  // fo char conversion
#include "..\browseui\legacy.h"  // for CITIDM_GETFOLDERSEARCHES
#include "varutil.h"
#include <wininet.h>

/////////////////////////////////////////////////////////////////////////////
// CSearchAssistantOC

//  If you change this, change browseui also.
const WCHAR c_wszThisBandIsYourBand[] = L"$$SearchBand$$";

//  HKLM values
#define REG_SZ_SEARCH       L"Software\\Microsoft\\Internet Explorer\\Search"
#define REG_SZ_SEARCHASSIST L"SearchAssistant"
#define REG_SZ_SEARCHCFG    L"CustomizeSearch"

//  HKCU values
#define REG_SZ_IE_MAIN      L"Software\\Microsoft\\Internet Explorer\\Main" 
#define REG_SZ_IE_SEARCURL  L"Software\\Microsoft\\Internet Explorer\\SearchURL"
#define REG_SZ_SEARCHBAR    L"Search Bar"
#define REG_SZ_USECUSTOM    L"Use Custom Search URL"
#define REG_SZ_AUTOSEARCH   L"AutoSearch"
#define REG_SZ_PROVIDER     L"Provider"
#define REG_SZ_USE_SEARCH_COMP  L"Use Search Asst"

#define SAOC_VERSION        2

const WCHAR c_wszSearchProps[] = REG_SZ_SEARCH L"\\SearchProperties";

STDAPI_(VARIANT_BOOL) UseCustomInternetSearch()
{
    VARIANT_BOOL bRet;
    DWORD dwVal;
    DWORD cbVal = sizeof(dwVal);
    
    if ((SHGetValueW(HKEY_CURRENT_USER, 
                    REG_SZ_IE_MAIN, 
                    REG_SZ_USECUSTOM, 
                    NULL, 
                    &dwVal, 
                    &cbVal) == ERROR_SUCCESS) &&
        (FALSE != dwVal))
    {
        bRet = VARIANT_TRUE;
    }
    else
    {
        bRet = VARIANT_FALSE;
    }

    return bRet;
}

STDAPI_(BOOL) GetSearchAssistantUrlW(LPWSTR pwszUrl, int cchUrl, BOOL bSubstitute, BOOL bCustomize)
{
    BOOL bResult;
    WCHAR wszUrlTmp[MAX_URL_STRING];
    WCHAR *pwszUrlRead;
    DWORD cb;

    ASSERT(pwszUrl);
    *pwszUrl = 0;

    if (bSubstitute)
    {
        cb = sizeof(wszUrlTmp);
        pwszUrlRead = wszUrlTmp;
    }
    else
    {
        cb = cchUrl * sizeof(WCHAR);
        pwszUrlRead = pwszUrl;
    }
    
    bResult = SHGetValueW(HKEY_LOCAL_MACHINE, 
                          REG_SZ_SEARCH, 
                          bCustomize ? REG_SZ_SEARCHCFG : REG_SZ_SEARCHASSIST,
                          NULL, (BYTE *)pwszUrlRead, &cb) == ERROR_SUCCESS;
    if (bResult && bSubstitute)
    {
        bResult = SUCCEEDED(URLSubstitution(wszUrlTmp, pwszUrl, cchUrl, URLSUB_ALL));
    }

    return bResult;
}

STDAPI_(BOOL) GetDefaultInternetSearchUrlW(LPWSTR pwszUrl, int cchUrl, BOOL bSubstitute)
{
    BOOL bResult = FALSE;
    DWORD cb;

    ASSERT(pwszUrl);
    *pwszUrl = 0;

    if (UseCustomInternetSearch())
    {
        //  First try the user specific value
        cb = cchUrl * sizeof(TCHAR);
        bResult = SHGetValueW(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_SEARCHBAR, 
                             NULL, (BYTE *)pwszUrl, &cb) == ERROR_SUCCESS;
    }
    
    if (!bResult)
    {
        bResult = GetSearchAssistantUrlW(pwszUrl, cchUrl, bSubstitute, FALSE);
    }

    return bResult;
}

STDAPI_(BOOL) GetSearchAssistantUrlA(LPSTR pszUrl, int cchUrl, BOOL bSubstitute, BOOL bCustomize)
{
    WCHAR wszUrl[INTERNET_MAX_URL_LENGTH];

    BOOL bResult = GetSearchAssistantUrlW(wszUrl, ARRAYSIZE(wszUrl), bSubstitute, bCustomize);

    SHUnicodeToAnsi(wszUrl, pszUrl, cchUrl);

    return bResult;
}

STDAPI_(BOOL) GetDefaultInternetSearchUrlA(LPSTR pszUrl, int cchUrl, BOOL bSubstitute)
{
    WCHAR wszUrl[INTERNET_MAX_URL_LENGTH];

    BOOL bResult = GetDefaultInternetSearchUrlW(wszUrl, ARRAYSIZE(wszUrl), bSubstitute);

    SHUnicodeToAnsi(wszUrl, pszUrl, cchUrl);

    return bResult;
}

void SetDefaultInternetSearchUrlW(LPCWSTR pwszUrl)
{
    DWORD dwUseCustom = FALSE;
    DWORD cb;
    
    if ((NULL != pwszUrl) && (0 != *pwszUrl))
    {
        cb = (lstrlenW(pwszUrl) + 1) * sizeof(WCHAR);
        if (SHSetValueW(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_SEARCHBAR, REG_SZ,
                        pwszUrl, cb) == ERROR_SUCCESS)
        {
            dwUseCustom = TRUE;
        }
    }

    cb = sizeof(dwUseCustom);

    SHSetValueW(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_USECUSTOM, REG_DWORD, 
                &dwUseCustom, cb);
}

HRESULT CSearch_Create(GUID *pguid, BSTR bstrTitle, BSTR bstrUrl, ISearch **ppSearch)
{
    HRESULT hres = E_INVALIDARG;

    ASSERT(ppSearch);
    *ppSearch = NULL;

    if (bstrTitle && bstrUrl && pguid)
    {
        BSTR _bstrTitle = SysAllocString(bstrTitle);
        BSTR _bstrUrl   = SysAllocString(bstrUrl);

        if (_bstrTitle && _bstrUrl)
        {
            CSearch *ps = new CSearch(pguid, _bstrTitle, _bstrUrl);

            if (ps)
            {
                hres = ps->QueryInterface(IID_ISearch, (void **)ppSearch);
                ps->Release();
            }
        }
        else
        {
            if (_bstrTitle)
                SysFreeString(_bstrTitle);

            if (_bstrUrl)
                SysFreeString(_bstrUrl);

            hres = E_OUTOFMEMORY;
        }
    }
    return hres;
}

CSearch::CSearch(GUID *pguid, BSTR bstrTitle, BSTR bstrUrl) :
    _cRef(1), _bstrTitle(bstrTitle), _bstrUrl(bstrUrl),
    CImpIDispatch(LIBID_SHDocVw, 1, 1, IID_ISearch)
{
    SHStringFromGUID(*pguid, _szId, ARRAYSIZE(_szId));
}

CSearch::~CSearch()
{
    if (_bstrTitle)
        SysFreeString(_bstrTitle);

    if (_bstrUrl)
        SysFreeString(_bstrUrl);
}

STDMETHODIMP CSearch::QueryInterface(REFIID riid, void** ppv)
{
    static const QITAB qit[] = {
        QITABENT(CSearch, ISearch),
        QITABENTMULTI(CSearch, IDispatch, ISearch),
        { 0 },
    };

    return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) CSearch::AddRef()
{
    InterlockedIncrement(&_cRef);
    return _cRef;
}

STDMETHODIMP_(ULONG) CSearch::Release()
{
    if (InterlockedDecrement(&_cRef) > 0)
        return _cRef;

    delete this;
    return 0;
}

HRESULT CSearch::get_Title(BSTR *pbstrTitle)
{
    HRESULT hres = NOERROR;
    
    *pbstrTitle = SysAllocString(_bstrTitle);
    if (!*pbstrTitle)
        hres = E_OUTOFMEMORY;

    return hres;
}

HRESULT CSearch::get_Id(BSTR  *pbstrId)
{
    HRESULT hres = NOERROR;

    *pbstrId = SysAllocString(_szId);
    if (!*pbstrId)
        hres = E_OUTOFMEMORY;

    return hres;
}

HRESULT CSearch::get_Url(BSTR *pbstrUrl)
{
    HRESULT hres = NOERROR;
    
    *pbstrUrl = SysAllocString(_bstrUrl);
    if (!*pbstrUrl)
        hres = E_OUTOFMEMORY;

    return hres;
}

HRESULT CSearchCollection_Create(IFolderSearches *pfs, ISearches **ppSearches)
{
    HRESULT hres = E_INVALIDARG;
    
    ASSERT(ppSearches);
    *ppSearches = NULL;

    if (pfs)
    {
        CSearchCollection *psc = new CSearchCollection(pfs);

        if (psc)
        {
            hres = psc->QueryInterface(IID_ISearches, (void **)ppSearches);
            psc->Release();
        }
        else
            hres = E_OUTOFMEMORY;
    }
    
    return hres;
}

CSearchCollection::CSearchCollection(IFolderSearches *pfs) :
    _cRef(1), CImpIDispatch(LIBID_SHDocVw, 1, 1, IID_ISearches)
{
    GUID guid;

    if (SUCCEEDED(pfs->DefaultSearch(&guid)))
        SHStringFromGUID(guid, _szDefault, ARRAYSIZE(_szDefault));
    
    _hdsaItems = DSA_Create(SIZEOF(URLSEARCH), 4);
    if (_hdsaItems)
    {
        IEnumUrlSearch *penum;

        if (SUCCEEDED(pfs->EnumSearches(&penum)))
        {
            URLSEARCH    us;
            ULONG        cElt;

            penum->Reset();
            while (S_OK == penum->Next(1, &us, &cElt) && 1 == cElt)
                DSA_AppendItem(_hdsaItems, &us);

            penum->Release();
        }
    }
}

CSearchCollection::~CSearchCollection()
{
    DSA_Destroy(_hdsaItems);
    _hdsaItems = NULL;
}

STDMETHODIMP CSearchCollection::QueryInterface(REFIID riid, void** ppv)
{
    static const QITAB qit[] = {
        QITABENT(CSearchCollection, ISearches),
        QITABENTMULTI(CSearchCollection, IDispatch, ISearches),
        { 0 },
    };

    return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) CSearchCollection::AddRef()
{
    InterlockedIncrement(&_cRef);
    return _cRef;
}

STDMETHODIMP_(ULONG) CSearchCollection::Release()
{
    if (InterlockedDecrement(&_cRef) > 0)
        return _cRef;

    delete this;
    return 0;
}

STDMETHODIMP CSearchCollection::get_Count(long *plCount)
{
    *plCount = 0;
    
    if (_hdsaItems)
    {
        *plCount =  DSA_GetItemCount(_hdsaItems);
    }
    return S_OK;
}

STDMETHODIMP CSearchCollection::get_Default(BSTR *pbstrDefault)
{
    HRESULT hres = E_OUTOFMEMORY;
    
    *pbstrDefault = SysAllocString(_szDefault);
    if (*pbstrDefault)
        hres = S_OK;
    
    return hres;
}

STDMETHODIMP CSearchCollection::Item(VARIANT index, ISearch **ppid)
{
    HRESULT hres = E_NOTIMPL;
    
    *ppid = NULL;
    
    switch (index.vt)
    {
        case VT_I2:
            index.lVal = (long)index.iVal;
            // And fall through...

        case VT_I4:
            if ((index.lVal >= 0) && (index.lVal < DSA_GetItemCount(_hdsaItems)))
            {
                LPURLSEARCH pus;

                pus = (LPURLSEARCH)DSA_GetItemPtr(_hdsaItems, index.lVal);
                ASSERT(pus);
                
                hres = CSearch_Create(&pus->guid, pus->wszName, pus->wszUrl, ppid);
            }

            break;
#if 0
        // should we worry about this one?
        case VT_BSTR:
#endif
    }

    return hres;
}

STDMETHODIMP CSearchCollection::_NewEnum(IUnknown **ppunk)
{
    *ppunk = NULL;
    return E_NOTIMPL;
}


CSearchAssistantOC::CSearchAssistantOC()
    :   m_punkSite(NULL)
{
#ifdef UNIX
    m_dwSafety = 0;
#endif
}

CSearchAssistantOC::~CSearchAssistantOC()
{
    ATOMICRELEASE(m_pSearchBandTBHelper);
    ATOMICRELEASE(m_punkSite);
}

HRESULT CSearchAssistantOC::OnDraw(ATL_DRAWINFO& di)
{
    return S_OK;
}

STDMETHODIMP CSearchAssistantOC::SetClientSite(IOleClientSite *pClientSite)
{
    if (NULL != pClientSite)
    {
        HRESULT hr;       
        IWebBrowser2 *pWebBrowser2;

        hr = IUnknown_QueryService(pClientSite, SID_SWebBrowserApp, IID_IWebBrowser2, 
                                   (void **)&pWebBrowser2);
        if (SUCCEEDED(hr))
        {
            BSTR bstrProp = SysAllocString(c_wszThisBandIsYourBand);
            if (bstrProp)
            {
                VARIANT var;
                hr = pWebBrowser2->GetProperty(bstrProp, &var);
                if (SUCCEEDED(hr))
                {
                    if (var.vt == VT_UNKNOWN)
                    {
                        ATOMICRELEASE(m_pSearchBandTBHelper);

                        hr = var.punkVal->QueryInterface(IID_ISearchBandTBHelper, 
                                                         (void **)&m_pSearchBandTBHelper);
                        ASSERT(SUCCEEDED(hr));

                        if (m_pSearchBandTBHelper)
                            m_pSearchBandTBHelper->SetOCCallback(this);
                    }

                    VariantClear(&var);
                }

                SysFreeString(bstrProp);
            }
            
            pWebBrowser2->Release();
        }
    }
    else
    {
        if (NULL != m_pSearchBandTBHelper)
        {
            m_pSearchBandTBHelper->SetOCCallback(NULL);
            ATOMICRELEASE(m_pSearchBandTBHelper);
        }
    }
    return IOleObjectImpl<CSearchAssistantOC>::SetClientSite(pClientSite);
}

STDMETHODIMP CSearchAssistantOC::QueryStatus(const GUID *pguidCmdGroup,
                                             ULONG cCmds, 
                                             OLECMD prgCmds[],
                                             OLECMDTEXT *pCmdText)
{
    return E_NOTIMPL;
}
    
STDMETHODIMP CSearchAssistantOC::Exec(const GUID *pguidCmdGroup,
                                      DWORD nCmdID, 
                                      DWORD nCmdexecopt,
                                      VARIANT *pvaIn,
                                      VARIANT *pvaOut)
{
    HRESULT hr = E_UNEXPECTED;
    
    if (NULL == pguidCmdGroup)
    {
        switch (nCmdID)
        {
            case SBID_SEARCH_NEXT:
                if ((NULL != pvaIn) && (pvaIn->vt == VT_I4))
                {
                    Fire_OnNextMenuSelect(pvaIn->lVal);

                    hr = S_OK;
                }
                else
                {
                    hr = E_INVALIDARG;
                }
                break;

            case SBID_SEARCH_NEW:
                if (NULL != pvaOut)
                {
                    m_bEventHandled = VARIANT_FALSE;

                    Fire_OnNewSearch();

                    pvaOut->vt = VT_BOOL;
                    pvaOut->boolVal = m_bEventHandled;

                    hr = S_OK;
                }
                else
                {
                    hr = E_INVALIDARG;
                }
                break;
        }
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::AddNextMenuItem(BSTR bstrText, long idItem)
{
    HRESULT hr;

    if (IsTrustedSite())
    {
        if (NULL != m_pSearchBandTBHelper)
        {
            hr = m_pSearchBandTBHelper->AddNextMenuItem(bstrText, idItem);

            ASSERT(SUCCEEDED(hr));

        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }
    return hr;
}

STDMETHODIMP CSearchAssistantOC::ResetNextMenu()
{
    HRESULT hr;

    if (IsTrustedSite())
    {
        if (NULL != m_pSearchBandTBHelper)
        {
            hr = m_pSearchBandTBHelper->ResetNextMenu();
            
            ASSERT(SUCCEEDED(hr));
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}


STDMETHODIMP CSearchAssistantOC::SetDefaultSearchUrl(BSTR bstrUrl)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        SetDefaultInternetSearchUrlW(bstrUrl);
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }
    
    return hr;
}

STDMETHODIMP CSearchAssistantOC::NavigateToDefaultSearch()
{
    HRESULT hr;
    

    IWebBrowser2 *pWebBrowser2;

    hr = IUnknown_QueryService(m_spClientSite, SID_SWebBrowserApp, IID_IWebBrowser2, 
                               (void **)&pWebBrowser2);
    if (SUCCEEDED(hr))
    {
        WCHAR wszUrl[INTERNET_MAX_URL_LENGTH];

        if (GetDefaultInternetSearchUrlW(wszUrl, ARRAYSIZE(wszUrl), TRUE))
        {
            BSTR bstrUrl = SysAllocString(wszUrl);


            if (NULL != bstrUrl) 
            {
                VARIANT varFrame;
                varFrame.vt = VT_BSTR;
                varFrame.bstrVal = SysAllocString(L"_search");
                if (NULL != varFrame.bstrVal)
                {
                    hr = pWebBrowser2->Navigate(bstrUrl, NULL, &varFrame, NULL, NULL);

                    ASSERT(SUCCEEDED(hr));

                    SysFreeString(varFrame.bstrVal);
                }

                SysFreeString(bstrUrl);
            }
        }
        pWebBrowser2->Release();
    }
 
    return S_OK;
}

typedef struct _GUIDREST
{
    const GUID *  pguid;
    RESTRICTIONS  rest;
} GUIDREST;

HRESULT CSearchAssistantOC::IsRestricted(BSTR bstrGuid, VARIANT_BOOL *pVal)
{
    HRESULT hr;
    GUID guid;

    if (IsTrustedSite())
    {
        *pVal = VARIANT_FALSE; // default to not restricted
        if (SUCCEEDED(SHCLSIDFromString(bstrGuid, &guid)))
        {
            // find computer is special because if it restricted then we show
            // it else don't show it (restriction name is HASFINDCOMPUTER
            if (IsEqualGUID(guid, SRCID_SFindComputer))
            {
                if (!SHRestricted(REST_HASFINDCOMPUTERS))
                    *pVal = VARIANT_TRUE;
            }
            else
            {
                static GUIDREST agr[] = 
                {
                    {&SRCID_SFileSearch, REST_NOFIND},
                    // rest_nofindprinter does not exist yet
                    //{&SRCID_SFindPrinter, REST_NOFINDPRINTER},
                };

                for (int i=0; i < ARRAYSIZE(agr); i++)
                {
                    if (IsEqualGUID(guid, *agr[i].pguid))
                    {
                        if (SHRestricted(agr[i].rest))
                            *pVal = VARIANT_TRUE;
                        break;
                    }
                }
            }
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

HRESULT CSearchAssistantOC::get_ShellFeaturesEnabled(VARIANT_BOOL *pVal)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        if (pVal)
        {
            *pVal = (GetUIVersion() >= 5) ? VARIANT_TRUE : VARIANT_FALSE;
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }
    
    return hr;
}

HRESULT CSearchAssistantOC::get_SearchAssistantDefault(VARIANT_BOOL *pVal)
{
    HRESULT hr;

    if (IsTrustedSite())
    {
        if (pVal)
        {
            *pVal = !UseCustomInternetSearch();
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }
    
    return hr;
}

STDMETHODIMP CSearchAssistantOC::get_Searches(ISearches **ppid)
{
    HRESULT hr;
    *ppid = NULL;

    if (IsTrustedSite())
    {
        IServiceProvider *psp;
        hr = IUnknown_QueryService(m_spClientSite, SID_STopLevelBrowser, IID_IServiceProvider, (void**)&psp);
        if (SUCCEEDED(hr))
        {
            IOleCommandTarget *pct;

            hr = psp->QueryService(SID_SExplorerToolbar, IID_IOleCommandTarget, (void **)&pct);
            if (SUCCEEDED(hr))
            {
                VARIANTARG var = {0};
                
                hr = pct->Exec(&CGID_PrivCITCommands, CITIDM_GETFOLDERSEARCHES, 0, NULL, &var);
                if (SUCCEEDED(hr))
                {
                    IFolderSearches *pfs;

                    ASSERT(var.vt == VT_UNKNOWN && var.punkVal);
                    hr = var.punkVal->QueryInterface(IID_IFolderSearches, (void **)&pfs);
                    if (SUCCEEDED(hr))
                    {
                        hr = CSearchCollection_Create(pfs, ppid);
                        pfs->Release();
                    }
                    var.punkVal->Release();
                }
                pct->Release();
                
                hr = S_OK;
            }

            psp->Release();
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::get_InWebFolder(VARIANT_BOOL *pVal)
{
    HRESULT hr;

    if (IsTrustedSite())
    {
        ASSERT(pVal);
        *pVal = VARIANT_FALSE;

        IBrowserService2 *pbs;
        
        hr = IUnknown_QueryService(m_spClientSite, SID_STopLevelBrowser, IID_IBrowserService2, (void **)&pbs);

        if (SUCCEEDED(hr))
        {
            ITEMIDLIST *pidl;

            hr = pbs->GetPidl(&pidl);

            if (SUCCEEDED(hr))
            {
                // REARCHITECT: Don't use ILIsWeb().  We should use IShellFolder2::GetDefaultSearchGUID() and
                //   test for SRCID_SWebSearch vs. SRCID_SFileSearch/SRCID_SFindComputer/SRCID_SFindPrinter.
                //   This is because Shell Extensions need a way to indicate what kind of search they want
                //   and ILIsWeb() doesn't provide that.  An example of this is "Web Folders" won't return
                //   TRUE from ILIsWeb().  The use of ILIsWeb() should be limited.
                if (ILIsWeb(pidl))
                {
                    *pVal = VARIANT_TRUE;
                }
                
                ILFree(pidl);
            }
            pbs->Release();
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }
    return hr;
}

void GetPerLocalePath(WCHAR *pwszKeyName, int cchKeyName)
{
    ASSERT(cchKeyName >= (ARRAYSIZE(c_wszSearchProps) + 1));
    
    StrCpyNW(pwszKeyName, c_wszSearchProps, cchKeyName);
    *(pwszKeyName + (ARRAYSIZE(c_wszSearchProps) - 1)) = L'\\';

    GetWebLocaleAsRFC1766(pwszKeyName + ARRAYSIZE(c_wszSearchProps), 
                          cchKeyName - (ARRAYSIZE(c_wszSearchProps)));
}

STDMETHODIMP CSearchAssistantOC::PutProperty(VARIANT_BOOL bPerLocale, BSTR bstrName, BSTR bstrValue)
{
    HRESULT hr;

    if (IsTrustedSite())
    {
        HKEY hkey;
        LPCWSTR pwszKeyName;
        WCHAR wszKeyName[MAX_PATH];
        DWORD dwDisposition;

        if (bPerLocale)
        {
            GetPerLocalePath(wszKeyName, ARRAYSIZE(wszKeyName));
            pwszKeyName = wszKeyName;
        }
        else
        {
            pwszKeyName = c_wszSearchProps;
        }

        if (RegCreateKeyExW(HKEY_CURRENT_USER, pwszKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, 
                           KEY_WRITE, NULL, &hkey, &dwDisposition) == ERROR_SUCCESS)
        {
            if ((NULL != bstrValue) && (bstrValue[0] != 0))
            {
                RegSetValueExW(hkey, bstrName, 0, REG_BINARY, (LPBYTE)bstrValue, 
                               SysStringByteLen(bstrValue));
            }
            else
            {
                //  Empty or NULL string means remove the property
                RegDeleteValue(hkey, bstrName);
            }
            RegCloseKey(hkey);
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }
    return hr;
}

STDMETHODIMP CSearchAssistantOC::GetProperty(VARIANT_BOOL bPerLocale, BSTR bstrName, BSTR *pbstrValue)
{
    HRESULT hr;

    if (NULL != pbstrValue)
    {
        *pbstrValue = NULL;
        
        if (IsTrustedSite())
        {
            HKEY hkey;
            LPCWSTR pwszKeyName;
            WCHAR wszKeyName[MAX_PATH];

            if (bPerLocale)
            {
                GetPerLocalePath(wszKeyName, ARRAYSIZE(wszKeyName));
                pwszKeyName = wszKeyName;
            }
            else
            {
                pwszKeyName = c_wszSearchProps;
            }

            if (RegOpenKeyExW(HKEY_CURRENT_USER, pwszKeyName, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
            {
                DWORD dwType;
                DWORD cbSize;
                
                if ((RegQueryValueExW(hkey, bstrName, NULL, &dwType, NULL, &cbSize) == ERROR_SUCCESS) &&
                    (dwType == REG_BINARY))
                {
                    BSTR bstrValue = SysAllocStringByteLen(NULL, cbSize);

                    if (NULL != bstrValue)
                    {
                        if (RegQueryValueExW(hkey, bstrName, NULL, &dwType, (LPBYTE)bstrValue, &cbSize) == ERROR_SUCCESS)
                        {
                            *pbstrValue = bstrValue;
                        }
                        else
                        {
                            SysFreeString(bstrValue);
                        }
                    }
                }
                
                RegCloseKey(hkey);
            }
            hr = S_OK;
        }
        else
        {
            hr = E_ACCESSDENIED;
        }
    }
    else
    {
        hr = E_INVALIDARG;
    }
    return hr;
}

STDMETHODIMP CSearchAssistantOC::put_EventHandled(VARIANT_BOOL bHandled)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        m_bEventHandled = bHandled;
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::GetSearchAssistantURL(VARIANT_BOOL bSubstitute, VARIANT_BOOL bCustomize, BSTR *pbstrValue)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        if (NULL != pbstrValue)
        {
            WCHAR wszUrl[INTERNET_MAX_URL_LENGTH];

            if (GetSearchAssistantUrlW(wszUrl, ARRAYSIZE(wszUrl), bSubstitute, bCustomize))
            {
                *pbstrValue = SysAllocString(wszUrl);
            }
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::NotifySearchSettingsChanged()
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        SendShellIEBroadcastMessage(WM_WININICHANGE, 0, (LPARAM)SEARCH_SETTINGS_CHANGED, 3000);
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::put_ASProvider(BSTR Provider)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        if (Provider)
        {
            DWORD dwRet = SHSetValueW(HKEY_CURRENT_USER, REG_SZ_IE_SEARCURL, REG_SZ_PROVIDER, REG_SZ,
                                      Provider, (lstrlenW(Provider) + 1) * sizeof(WCHAR));

            ASSERT(ERROR_SUCCESS == dwRet);
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::get_ASProvider(BSTR *pProvider)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        if (NULL != pProvider)
        {
            HKEY hkey;

            if (ERROR_SUCCESS == RegOpenKeyExW(HKEY_CURRENT_USER, REG_SZ_IE_SEARCURL, 0, KEY_READ, &hkey))
            {
                DWORD dwType;
                DWORD dwSize;
                
                if ((ERROR_SUCCESS == RegQueryValueExW(hkey, REG_SZ_PROVIDER, NULL, 
                                                       &dwType, NULL, &dwSize)) && 
                                                       (REG_SZ == dwType))
                {
                    *pProvider = SysAllocStringByteLen(NULL, dwSize);
                    if (NULL != *pProvider)
                    {
                        if (ERROR_SUCCESS != RegQueryValueExW(hkey, REG_SZ_PROVIDER, NULL, 
                                                              &dwType, (LPBYTE)*pProvider, &dwSize))
                        {
                            *pProvider = 0;
                        }
                    }
                }
                RegCloseKey(hkey);
            }
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::put_ASSetting(int Setting)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        DWORD dwRet = SHSetValueW(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_AUTOSEARCH, REG_DWORD,
                                  &Setting, sizeof(DWORD));

        ASSERT(ERROR_SUCCESS == dwRet);
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::get_ASSetting(int *pSetting)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        if (NULL != pSetting)
        {
            DWORD dwSize = sizeof(int);

            *pSetting = -1;
            
            DWORD dwRet = SHGetValueW(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_AUTOSEARCH, NULL,
                                      pSetting, &dwSize);

            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

BOOL CSearchAssistantOC::IsTrustedSite()
{
    if (!m_bSafetyInited && m_spClientSite)
    {
        m_bSafetyInited = TRUE;

        IHTMLDocument2 *pHTMLDocument2;
      
        HRESULT hr = GetHTMLDoc2(m_spClientSite, &pHTMLDocument2);

        if (SUCCEEDED(hr))
        {
            ASSERT(NULL != pHTMLDocument2);

            IHTMLLocation *pHTMLLocation;

            hr = pHTMLDocument2->get_location(&pHTMLLocation);

            if (SUCCEEDED(hr) && (NULL != pHTMLLocation))
            {           
                BSTR bstrUrl;

                pHTMLLocation->get_href(&bstrUrl);

                if (SUCCEEDED(hr) && (NULL != bstrUrl))
                {
                    HKEY hkey;
                    
                    //  FEATURE (tnoonan)
                    //  This code is duped with CSearchBand::_IsSafeUrl in browseui
                    
                    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\SafeSites", 0, KEY_READ, &hkey) == ERROR_SUCCESS)
                    {        
                        WCHAR wszValue[MAX_PATH];
                        WCHAR wszData[MAX_URL_STRING];
                        WCHAR wszExpandedUrl[MAX_URL_STRING];
                        DWORD cbData = SIZEOF(wszData);
                        DWORD cchValue = ARRAYSIZE(wszValue);

                        for (int i=0; RegEnumValueW(hkey, i, wszValue, &cchValue, NULL, NULL, (LPBYTE)wszData, &cbData) == ERROR_SUCCESS; i++)
                        {
                            if (SHExpandEnvironmentStringsW(wszData, wszExpandedUrl, ARRAYSIZE(wszExpandedUrl)) > 0)
                            {
                                cchValue = ARRAYSIZE(wszExpandedUrl);
                                if (SUCCEEDED(UrlCanonicalizeW(wszExpandedUrl, wszExpandedUrl, &cchValue, 0)))
                                {
                                    if (cchValue > 0)
                                    {
                                        BOOL bRet;
                                        if (wszExpandedUrl[cchValue-1] == L'*')
                                        {
                                            bRet = StrCmpNIW(bstrUrl, wszExpandedUrl, cchValue - 1) == 0;
                                        }
                                        else
                                        {
                                            bRet = StrCmpIW(bstrUrl, wszExpandedUrl) == 0;
                                        }

                                        m_bIsTrustedSite = bRet ? TRUE : FALSE;
                                        
                                        if (m_bIsTrustedSite)
                                            break;
                                    }
                                }
                                cbData = SIZEOF(wszData);
                                cchValue = ARRAYSIZE(wszValue);
                            }
                        }
                        RegCloseKey(hkey);
                    }

                    SysFreeString(bstrUrl);
                }
                
                pHTMLLocation->Release();
            }
            
            pHTMLDocument2->Release();
        }
    }

    return m_bIsTrustedSite;
}

HRESULT CSearchAssistantOC::UpdateRegistry(BOOL bRegister)
{
    //this control uses selfreg.inx, not the ATL registry goo
    return S_OK;
}

STDMETHODIMP CSearchAssistantOC::FindOnWeb()
{
    if (!IsTrustedSite() && m_punkSite==NULL)
        return E_ACCESSDENIED ;

    return ShowSearchBand( SRCID_SWebSearch ) ;
}

STDMETHODIMP CSearchAssistantOC::FindFilesOrFolders()
{
    if (!IsTrustedSite() && m_punkSite==NULL)
        return E_ACCESSDENIED ;

    return ShowSearchBand( SRCID_SFileSearch ) ;
}

STDMETHODIMP CSearchAssistantOC::FindComputer()
{
    if (!IsTrustedSite() && m_punkSite==NULL)
        return E_ACCESSDENIED ;

    return ShowSearchBand( SRCID_SFindComputer ) ;
}

STDMETHODIMP CSearchAssistantOC::FindPrinter()
{
    if (!IsTrustedSite() && m_punkSite==NULL)
        return E_ACCESSDENIED ;

    HRESULT hr = E_FAIL;
    IShellDispatch2* psd2;
    if( SUCCEEDED( (hr = CoCreateInstance( CLSID_Shell, NULL, CLSCTX_INPROC_SERVER,
                                           IID_IShellDispatch2, (void**)&psd2 )) ) )
    {
        hr = psd2->FindPrinter( NULL, NULL, NULL ) ;
        psd2->Release();
    }
    return hr ;
}

STDMETHODIMP CSearchAssistantOC::FindPeople()
{
    if (!IsTrustedSite() && m_punkSite==NULL)
    {
        return E_ACCESSDENIED;
    }

    SHELLEXECUTEINFO sei = {0};
    sei.cbSize = sizeof(SHELLEXECUTEINFO);
    sei.hwnd = HWND_DESKTOP;
    sei.lpVerb = TEXT("open");
    sei.lpFile = TEXT("wab.exe");
    sei.lpParameters = TEXT("/find");
    sei.lpDirectory = NULL;
    sei.nShow = SW_SHOWNORMAL;

    if (!ShellExecuteEx(&sei))
    {
        return HRESULT_FROM_WIN32(GetLastError());
    }
    else
    {
        return S_OK;
    }
}

// Wininet helper method to retry autodetection

// check to make sure that the hosting page is on
// the local computer.
// stolen from the zones code by joshco
//
STDMETHODIMP CSearchAssistantOC::LocalZoneCheck( )
{
    HRESULT hr = E_ACCESSDENIED;

    //  Return S_FALSE if we don't have a host site since we have no way of doing a 
    //  security check.  This is as far as VB 5.0 apps get.
    if (!m_spClientSite)
        return S_FALSE;

    //  1)  Get an IHTMLDocument2 pointer
    //  2)  Get URL from doc
    //  3)  Create security manager
    //  4)  Check if doc URL zone is local, if so everything's S_OK
    //  5)  Otherwise, get and compare doc URL SID to requested URL SID

    IHTMLDocument2 *pHtmlDoc;
    if (SUCCEEDED(GetHTMLDoc2(m_spClientSite, &pHtmlDoc)))
    {
        ASSERT(pHtmlDoc);
        BSTR bstrDocUrl;
        if (SUCCEEDED(pHtmlDoc->get_URL(&bstrDocUrl)))
        {
            ASSERT(bstrDocUrl);
            IInternetSecurityManager *pSecMgr;

            if (SUCCEEDED(CoCreateInstance(CLSID_InternetSecurityManager, 
                                           NULL, 
                                           CLSCTX_INPROC_SERVER,
                                           IID_IInternetSecurityManager, 
                                           (void **)&pSecMgr)))
            {
                ASSERT(pSecMgr);
                DWORD dwZoneID = URLZONE_UNTRUSTED;
                if (SUCCEEDED(pSecMgr->MapUrlToZone(bstrDocUrl, &dwZoneID, 0)))
                {
                    if (dwZoneID == URLZONE_LOCAL_MACHINE)
                        hr = S_OK;
                }
               
                pSecMgr->Release();
            }
            SysFreeString(bstrDocUrl);
        }
        pHtmlDoc->Release();
    }
    else
    {
        //  If we don't have an IHTMLDocument2 we aren't running in a browser that supports
        //  our OM.  We shouldn't block in this case since we could potentially
        //  get here from other hosts (VB, WHS, etc.).
        hr = S_FALSE;
    }

    return hr;
}

// set flags so that the next navigate will cause 
// a proxy autodetection cycle
// used in dnserr.htm along with location.reload.
// added by joshco
//
STDMETHODIMP CSearchAssistantOC::NETDetectNextNavigate()
{
    HRESULT hr = S_FALSE;

 CHAR  szConnectionName[100];
 DWORD dwBufLen;
 DWORD dwFlags;
 BOOL fResult;

 if (  LocalZoneCheck() != S_OK ) {
     // some security problem.. time to bail.
    hr=E_ACCESSDENIED;
    goto error;
    }

 dwBufLen = sizeof(szConnectionName);

       // find the connection name via internetconnected state
       
 fResult = InternetGetConnectedStateExA(&dwFlags,  szConnectionName,dwBufLen, 0 );

 INTERNET_PER_CONN_OPTION_LISTA list;
 INTERNET_PER_CONN_OPTIONA option;
       
 list.dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTA);
 if(!fResult || (dwFlags & INTERNET_CONNECTION_LAN))
    {
        list.pszConnection = NULL;
    }
    else
    {
        list.pszConnection =  szConnectionName;
    }

 list.dwOptionCount = 1;
 list.pOptions = &option;
 option.dwOption = INTERNET_PER_CONN_FLAGS;
 dwBufLen= sizeof(list);

   // now call internetsetoption to do it..
   // first set this connectoid to enable autodetect
 if ( ! InternetQueryOptionA(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION ,
         &list,&dwBufLen) ) 
    {
           goto error;
    }
               
 option.Value.dwValue |= PROXY_TYPE_AUTO_DETECT ;

 if ( ! InternetSetOptionA(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION ,
        &list,sizeof(list))) 
   {
         goto error;
   }

 if ( ! InternetInitializeAutoProxyDll(0) ) {
         goto error;
   }

 //  Now set the autodetect flags for this connectoid to
 //  do a passive detect and shut itself off if it doesnt work
 option.dwOption = INTERNET_PER_CONN_AUTODISCOVERY_FLAGS;
 
 if ( ! InternetQueryOptionA(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION ,
         &list,&dwBufLen) ) 
    {
           goto error;
    }
               
 option.Value.dwValue &= ~(AUTO_PROXY_FLAG_DETECTION_RUN) ;

 if ( ! InternetSetOptionA(NULL, INTERNET_OPTION_PER_CONNECTION_OPTION ,
        &list,sizeof(list))) 
   {
         goto error;
   }


 
 if ( ! InternetSetOptionA(NULL, INTERNET_OPTION_SETTINGS_CHANGED,NULL, 0) ) {
         goto error;
   }



 hr=S_OK;
 error: ;
     
 return hr;
}

STDMETHODIMP CSearchAssistantOC::PutFindText(BSTR FindText)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        IServiceProvider *pServiceProvider;
        
        hr = IUnknown_QueryService(m_pSearchBandTBHelper, 
                                   SID_SProxyBrowser, 
                                   IID_IServiceProvider, 
                                   (void **)&pServiceProvider);
        if (SUCCEEDED(hr))
        {
            IWebBrowser2 *pWebBrowser2;
            hr = pServiceProvider->QueryService(SID_SWebBrowserApp, 
                                                IID_IWebBrowser2, 
                                                (void **)&pWebBrowser2);
            if (SUCCEEDED(hr))
            {
                ::PutFindText(pWebBrowser2, FindText);
                pWebBrowser2->Release();
            }
            pServiceProvider->Release();
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}

STDMETHODIMP CSearchAssistantOC::get_Version(int *pVersion)
{
    if (NULL != pVersion)
    {
        *pVersion = SAOC_VERSION;
    }
    return S_OK;
}

// x_hex_digit and URLEncode were stolen from trident

inline int x_hex_digit(int c)
{
    if (c >= 0 && c <= 9)
    {
        return c + '0';
    }
    if (c >= 10 && c <= 15)
    {
        return c - 10 + 'A';
    }
    return '0';
}

/*
   The following array was copied directly from NCSA Mosaic 2.2
 */
static const unsigned char isAcceptable[96] =
/*   0 1 2 3 4 5 6 7 8 9 A B C D E F */
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0,    /* 2x   !"#$%&'()*+,-./  */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,    /* 3x  0123456789:;<=>?  */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,    /* 4x  @ABCDEFGHIJKLMNO  */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,    /* 5x  PQRSTUVWXYZ[\]^_  */
 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,    /* 6x  `abcdefghijklmno  */
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};   /* 7x  pqrstuvwxyz{\}~  
DEL */

// Performs URL-encoding of null-terminated strings. Pass NULL in pbOut
// to find buffer length required. Note that '\0' is not written out.

int URLEncode(char * pbOut, const char * pchIn)
{
    int     lenOut = 0;
    char *  pchOut = pbOut;

    ASSERT(pchIn);

    for (; *pchIn; pchIn++, lenOut++)
    {
        if (*pchIn == ' ')
        {
            if (pchOut)
                *pchOut++ = '+';
        }
        else if (*pchIn >= 32 && *pchIn <= 127 && isAcceptable[*pchIn - 32])
        {
            if (pchOut)
                *pchOut++ = *pchIn;
        }
        else
        {
            if (pchOut)
                *pchOut++ = '%';
            lenOut++;

            if (pchOut)
                *pchOut++ = (char)x_hex_digit((*pchIn >> 4) & 0xf);
            lenOut++;

            if (pchOut)
                *pchOut++ = (char)x_hex_digit(*pchIn & 0xf);
        }
    }
    return lenOut;
}

STDMETHODIMP CSearchAssistantOC::EncodeString(BSTR bstrValue, BSTR bstrCharSet, VARIANT_BOOL bUseUTF8, BSTR *pbstrResult)
{

    if ((NULL != bstrValue) && (NULL != pbstrResult))
    {
        HRESULT hr;
        IMultiLanguage2 *pMultiLanguage2;

        *pbstrResult = NULL;
        
        hr = CoCreateInstance(CLSID_CMultiLanguage, NULL, CLSCTX_INPROC_SERVER,
                              IID_IMultiLanguage2, (void**)&pMultiLanguage2);

        if (SUCCEEDED(hr))
        {
            UINT uiCodePage = CP_BOGUS;
            UINT cchVal = SysStringLen(bstrValue);
            DWORD dwMode = 0;

            if (!bUseUTF8)
            {
                //  We're not using UTF so try and get the code page from the 
                //  charset.
                
                MIMECSETINFO info;
                
                hr = pMultiLanguage2->GetCharsetInfo(bstrCharSet, &info);

                if (SUCCEEDED(hr))
                {                   
                    hr = pMultiLanguage2->ConvertStringFromUnicodeEx(&dwMode,
                                                                     info.uiCodePage,
                                                                     bstrValue,
                                                                     &cchVal,
                                                                     NULL,
                                                                     NULL,
                                                                     MLCONVCHARF_NOBESTFITCHARS,
                                                                     NULL);
                    if (S_OK == hr)
                    {
                        uiCodePage = info.uiCodePage;
                    }
                }
            }
            else
            {
                uiCodePage = CP_UTF_8;
            }

            if (uiCodePage == CP_BOGUS)
            {
                //  we have characters which don't work in the charset or the charset
                //  is unknown to MLang, maybe MLang can figure out a code page to use.
                
                IMLangCodePages *pMLangCodePages;

                //  When all else fails...
                uiCodePage = CP_ACP;

                hr = pMultiLanguage2->QueryInterface(IID_IMLangCodePages,
                                                     (void **)&pMLangCodePages);
                if (SUCCEEDED(hr))
                {
                    DWORD dwCodePages = 0;
                    long cchProcessed = 0;
                    UINT uiTmpCP = 0;
                    
                    if (SUCCEEDED(pMLangCodePages->GetStrCodePages(bstrValue, cchVal, 
                                                                   0, &dwCodePages,
                                                                   &cchProcessed)) 

                        &&

                        SUCCEEDED(pMLangCodePages->CodePagesToCodePage(dwCodePages,
                                                                       0,
                                                                       &uiTmpCP)))
                    {
                        uiCodePage = uiTmpCP;
                    }

                    pMLangCodePages->Release();
                }
            }

            dwMode = 0;

            UINT cbVal = 0;

            //  Ask MLang how big of a buffer we need
            hr = pMultiLanguage2->ConvertStringFromUnicode(&dwMode,
                                                           uiCodePage,
                                                           bstrValue,
                                                           &cchVal,
                                                           NULL,
                                                           &cbVal);

            if (SUCCEEDED(hr))
            {
                CHAR *pszValue = new CHAR[cbVal + 1];

                if (NULL != pszValue)
                {
                    //  Really convert the string
                    hr = pMultiLanguage2->ConvertStringFromUnicode(&dwMode,
                                                                   uiCodePage,
                                                                   bstrValue,
                                                                   &cchVal,
                                                                   pszValue,
                                                                   &cbVal);
                    if (SUCCEEDED(hr))
                    {
                        pszValue[cbVal] = 0;
                        
                        int cbEncVal = URLEncode(NULL, pszValue);
                        CHAR *pszEncVal = new CHAR[cbEncVal];

                        if (NULL != pszEncVal)
                        {
                            URLEncode(pszEncVal, pszValue);

                            int cchResult = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
                                                                pszEncVal, cbEncVal,
                                                                NULL, 0);

                            *pbstrResult = SysAllocStringLen(NULL, cchResult);

                            if (NULL != *pbstrResult)
                            {
                                MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
                                                    pszEncVal, cbEncVal,
                                                    *pbstrResult, cchResult);
                            }

                            delete [] pszEncVal;
                        }
                    }
                    delete [] pszValue;
                }
            }
            pMultiLanguage2->Release();
        }
    }


    return S_OK;
}

STDMETHODIMP CSearchAssistantOC::get_ShowFindPrinter(VARIANT_BOOL *pbShowFindPrinter)
{
    HRESULT hr;

    if (IsTrustedSite())
    {
        if (NULL != pbShowFindPrinter)
        {
            IShellDispatch2* psd;

            *pbShowFindPrinter = VARIANT_FALSE;

            if (SUCCEEDED(CoCreateInstance(CLSID_Shell, 0, CLSCTX_INPROC_SERVER, 
                                          IID_IShellDispatch2, (void**)&psd)))
            {
                BSTR bstrName = SysAllocString( L"DirectoryServiceAvailable");

                if (bstrName)
                {
                    VARIANT varRet = {0};
                    
                    if (SUCCEEDED(psd->GetSystemInformation(bstrName, &varRet)))
                    {
                        ASSERT(VT_BOOL == varRet.vt);
                        *pbShowFindPrinter = varRet.boolVal;
                    }
                    SysFreeString(bstrName);
                }
                psd->Release();
            }
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;
}


STDMETHODIMP CSearchAssistantOC::get_SearchCompanionAvailable(VARIANT_BOOL *pbAvailable)
{
    HRESULT hr = E_ACCESSDENIED;
    if (IsTrustedSite())
    {
        if (pbAvailable != NULL)
        {
            BOOL fSCAvailable = FALSE;
            // need to determine if search companion is available on system and supports user locale
            ISearchCompanionInfo*   psci;
            hr = CoCreateInstance(CLSID_SearchCompanionInfo, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(ISearchCompanionInfo, &psci));
            if (SUCCEEDED(hr))
            {
                hr = psci->IsSearchCompanionInetAvailable(&fSCAvailable);
                psci->Release();
            }
            *pbAvailable = fSCAvailable ? VARIANT_TRUE : VARIANT_FALSE;
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    return hr;
}

STDMETHODIMP CSearchAssistantOC::put_UseSearchCompanion(VARIANT_BOOL bUseSC)
{
    HRESULT hr = E_ACCESSDENIED;
    if (IsTrustedSite())
    {
        LPCTSTR pszUseSC = bUseSC ? TEXT("yes") : TEXT("no");
        SHSetValue(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_USE_SEARCH_COMP, REG_SZ, pszUseSC, CbFromCch(lstrlen(pszUseSC) + 1));

        if (bUseSC)
        {
            // disable "Use Custom Search URL" as SC won't launch if custom search URL is defined
            DWORD dwUseCustom = FALSE;
            SHSetValue(HKEY_CURRENT_USER, REG_SZ_IE_MAIN, REG_SZ_USECUSTOM, REG_DWORD, &dwUseCustom, sizeof(dwUseCustom));
        }
        
        hr = S_OK;
    }
    return hr;
}

STDMETHODIMP CSearchAssistantOC::get_UseSearchCompanion(VARIANT_BOOL *pbUseSC)
{
    HRESULT hr = E_ACCESSDENIED;
    if (IsTrustedSite())
    {
        if (pbUseSC != NULL)
        {
            BOOL fUseSC = SHRegGetBoolUSValue(REG_SZ_IE_MAIN, REG_SZ_USE_SEARCH_COMP, FALSE, FALSE);
            *pbUseSC = fUseSC ? VARIANT_TRUE : VARIANT_FALSE;
            hr = S_OK;
        }
        else
        {
            hr = E_INVALIDARG;
        }
    }
    return hr;
}


#ifdef ENABLE_THIS_FOR_IE5X

STDMETHODIMP CSearchAssistantOC::RefreshLocation(IDispatch *pLocation)
{
    HRESULT hr;
    
    if (IsTrustedSite())
    {
        if (NULL != pLocation)
        {
            IHTMLLocation *pHTMLLocation;

            IUnknown_QueryService(pLocation, IID_IHTMLLocation, IID_IHTMLLocation, (void **)&pHTMLLocation);

            if (pHTMLLocation)
            {
                pHTMLLocation->reload(VARIANT_TRUE);
                pHTMLLocation->Release();
            }
        }
        hr = S_OK;
    }
    else
    {
        hr = E_ACCESSDENIED;
    }

    return hr;

}

#endif
//-------------------------------------------------------------------------//
#define REG_SZ_SHELL_SEARCH TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\") \
                            TEXT("Explorer\\FindExtensions\\Static\\ShellSearch")
//-------------------------------------------------------------------------//
HRESULT GetSearchURLs( 
    IN REFGUID guidSearch, 
    OUT LPTSTR pszUrl, 
    IN DWORD cch, 
    OUT OPTIONAL LPTSTR pszUrlNavNew, 
    OUT DWORD cchNavNew, 
    OUT BOOL *pfRunInProcess )
{
    HRESULT hr = E_FAIL ;
    DWORD   cb ;
    DWORD   dwType ;
    DWORD   dwErr ;

    *pfRunInProcess = FALSE ;
    if( pszUrlNavNew && cchNavNew )
        *pszUrlNavNew = 0 ;

    if( IsEqualGUID( guidSearch, SRCID_SWebSearch ) )
    {
        if( GetDefaultInternetSearchUrlW( pszUrl, cch, TRUE ) )
            hr = S_OK ;
    }
    else
    {
        //  The shell search URL-eating stuff was adapted from
        //  CShellSearchExt::_GetSearchUrls() in browseui\browband.cpp, 
        //  and should be kept in sync.

        TCHAR szSubKey[32];
        HKEY  hkey, hkeySub;
        if( (dwErr = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_SZ_SHELL_SEARCH, 
                                   0, KEY_READ, &hkey )) != ERROR_SUCCESS )
            return HRESULT_FROM_WIN32( dwErr ) ;

        hr = E_FAIL ;

        for( int i = 0; 
             wnsprintf( szSubKey, ARRAYSIZE(szSubKey), TEXT("%d"), i ), 
                RegOpenKey(hkey, szSubKey, &hkeySub) == ERROR_SUCCESS ;
             i++ )
        {
            TCHAR szSearchGuid[MAX_PATH];

            cb = SIZEOF(szSearchGuid);
            
            if( SHGetValue( hkeySub, TEXT("SearchGUID"), NULL, &dwType, (BYTE*)szSearchGuid, &cb ) == ERROR_SUCCESS )
            {
                GUID guid;
                SHCLSIDFromString(szSearchGuid, &guid);

                if( IsEqualGUID( guid, guidSearch ) )
                {
                    cb = cch * sizeof(TCHAR);
                    if( SHGetValue( hkeySub, TEXT("SearchGUID\\Url"), NULL, 
                                    &dwType, (BYTE*)pszUrl, &cb ) == ERROR_SUCCESS )
                    {
                        if( pszUrlNavNew && cchNavNew )
                        {
                            // See if there is a secondary URL that we should navigate to
                            cb = cchNavNew * sizeof(TCHAR);
                            SHGetValue(hkeySub, TEXT("SearchGUID\\UrlNavNew"), NULL, &dwType, (BYTE*)pszUrlNavNew, &cb);
                        }

                        // try to grab the RunInProcess flag
                        *pfRunInProcess = (BOOL)SHRegGetIntW( hkeySub, L"RunInProcess", 0 );

                        RegCloseKey(hkeySub);
                        hr = S_OK ;
                        break;
                    }
                }
            }
            RegCloseKey(hkeySub);
        }
        RegCloseKey( hkey ) ;
    }
    return hr ;
}

STDMETHODIMP _IsShellSearchBand( REFGUID guidSearch )
{
    if (IsEqualGUID( guidSearch, SRCID_SFileSearch ) ||
        IsEqualGUID( guidSearch, SRCID_SFindComputer ) ||
        IsEqualGUID( guidSearch, SRCID_SFindPrinter ) )
        return S_OK;
    return S_FALSE;
}

//-------------------------------------------------------------------------//
//  Establishes the correct shell search dialog, etc.
STDMETHODIMP _ShowShellSearchBand( IWebBrowser2* pwb2, REFGUID guidSearch )
{
    ASSERT( pwb2 );
    ASSERT( S_OK == _IsShellSearchBand( guidSearch ) );

    HRESULT hr;
    VARIANT varBand;
    if (SUCCEEDED( (hr = InitBSTRVariantFromGUID(&varBand, CLSID_FileSearchBand)) ))
    {
        //  Retrieve the FileSearchBand's unknown from the browser frame as a VT_UNKNOWN property;
        //  (FileSearchBand initialized and this when he was created and hosted.)
        VARIANT varFsb;
        if (SUCCEEDED( (hr = pwb2->GetProperty( varBand.bstrVal, &varFsb )) ))
        {
            if (VT_UNKNOWN == varFsb.vt && varFsb.punkVal != NULL )
            {
                //  Retrieve the IFileSearchBand interface
                IFileSearchBand* pfsb;
                if (SUCCEEDED( (hr = varFsb.punkVal->QueryInterface( IID_IFileSearchBand, (LPVOID*)&pfsb )) ))
                {
                    //  Assign the correct search type to the band
                    VARIANT varSearchID;
                    if (SUCCEEDED( (hr = InitBSTRVariantFromGUID(&varSearchID, guidSearch)) ))
                    {
                        VARIANT      varNil = {0};
                        VARIANT_BOOL bNavToResults = VARIANT_FALSE ; 
                            // Note [scotthan]: we only navigate to results when we create a 
                            // new frame for the search, which we never do from srchasst.
                        pfsb->SetSearchParameters( &varSearchID.bstrVal, bNavToResults, &varNil, &varNil );
                        VariantClear( &varSearchID );
                    }
                    pfsb->Release();
                }
            }
            VariantClear( &varFsb );
        }
        VariantClear( &varBand );
    }
    return hr;
}

//-------------------------------------------------------------------------//
//  The goop to show a search band in the current browser frame.
//  6/1
HRESULT CSearchAssistantOC::ShowSearchBand( REFGUID guidSearch ) 
{
    HRESULT           hr = E_FAIL;
    TCHAR             szUrl[MAX_URL_STRING];
    TCHAR             szUrlNavNew[MAX_URL_STRING];
    CLSID             clsidBand;
    BOOL              fShellSearchBand = FALSE;
    BOOL              fRunInProcess = FALSE;
    IUnknown*         punkSite = m_punkSite ? m_punkSite : (IUnknown*)m_spClientSite;

    if( !punkSite )
        return E_UNEXPECTED ;

    //  Determine band class and whether the band supports navigation
    if( (fShellSearchBand = (S_OK == _IsShellSearchBand( guidSearch ))) )
    {
        if (SHRestricted(REST_NOFIND) && IsEqualGUID(guidSearch, SRCID_SFileSearch))
            return E_ACCESSDENIED;
        clsidBand = CLSID_FileSearchBand;
    }
    else
    {
        clsidBand = CLSID_SearchBand;
        //  we need to navigate to a search URL, grope the registry for that special URL
        if( FAILED( (hr= GetSearchURLs( guidSearch, szUrl, ARRAYSIZE(szUrl), 
                                        szUrlNavNew, ARRAYSIZE(szUrlNavNew), 
                                        &fRunInProcess )) ) )
            return hr;
    }
        
    //  FEATURE [scotthan]: this function will fail unless invoked from within a browser.
    //  This sits fine for now since SearchAsst is designed as a browser band.
    IWebBrowser2* pwb2;
    hr = IUnknown_QueryServiceForWebBrowserApp(punkSite, IID_PPV_ARG(IWebBrowser2, &pwb2));
    if (SUCCEEDED(hr))
    {
        SA_BSTRGUID bstrClsid;
        InitFakeBSTR(&bstrClsid, clsidBand);

        VARIANT var;
        var.bstrVal = bstrClsid.wsz;
        var.vt = VT_BSTR;

        VARIANT  varNil = {0};
            
        // show a search bar
        hr = pwb2->ShowBrowserBar(&var, &varNil, &varNil);
        if( SUCCEEDED( hr ) )
        {
            VARIANT varFlags;
            if( fShellSearchBand )
            {
                hr= _ShowShellSearchBand( pwb2, guidSearch );
            }
            else
            {
                varFlags.vt = VT_I4;
                varFlags.lVal = navBrowserBar;
                var.bstrVal = SysAllocString( T2W( szUrl ) ) ;
                var.vt = VT_BSTR ;

                // navigate the search bar to the correct url
                hr = pwb2->Navigate2(&var, &varFlags, &varNil, &varNil, &varNil);

                SysFreeString(var.bstrVal);
                    
                if( SUCCEEDED( hr ) )
                {
                    hr = pwb2->put_Visible( TRUE ) ;
                }
            }
        }
        pwb2->Release();
    }
    return hr;
}

STDMETHODIMP CSearchAssistantOC::SetSite( IUnknown* punkSite )
{
    ATOMICRELEASE(m_punkSite);
    if ((m_punkSite = punkSite) != NULL)
        m_punkSite->AddRef() ;
    return S_OK ;
}

STDMETHODIMP CSearchAssistantOC::GetSite( REFIID riid, void** ppvSite )
{
    if( !m_punkSite )
        return E_FAIL ;
    return m_punkSite->QueryInterface( riid, ppvSite ) ;
}

