#include "priv.h"
#include "sccls.h"
#include "dochost.h"
#include "resource.h"
#include "stdenum.h"
#include <idhidden.h>
#include "shdocfl.h"
#include <vdate.h>

#include <mluisupp.h>

#ifdef UNIX
#include "unixstuff.h"
#endif

HRESULT CDocObjectView_Create(IShellView** ppv, IShellFolder* psf, LPCITEMIDLIST pidl);


#define DM_STARTUP          0
#define DM_CDOFPDN          0       // CDocObjectFolder::ParseDisplayName

class CDocObjectFolder :    public IShellFolder2, 
                            public IPersistFolder2,
                            public IBrowserFrameOptions
{
public:
    CDocObjectFolder(LPCITEMIDLIST pidlRoot = NULL);

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID, void **);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    // IShellFolder
    STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
        ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);

    STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);

    STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
    STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj);
    STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
    STDMETHODIMP CreateViewObject (HWND hwnd, REFIID riid, void **ppvOut);
    STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut);
    STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
                                 REFIID riid, UINT * prgfInOut, void **ppvOut);
    STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
    STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, 
                           DWORD uFlags, LPITEMIDLIST * ppidlOut);
    // IShellFolder2
    STDMETHODIMP GetDefaultSearchGUID(LPGUID pGuid);
    STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum);
    STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay) { return E_NOTIMPL; };
    STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState) { return E_NOTIMPL; };
    STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv) { return E_NOTIMPL; };
    STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails){ return E_NOTIMPL; };
    STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid) { return E_NOTIMPL; };

    // IPersistFolder
    STDMETHODIMP GetClassID(LPCLSID pClassID);
    STDMETHODIMP Initialize(LPCITEMIDLIST pidl);

    // IPersistFolder2
    STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);
    
    // IBrowserFrameOptions
    STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions);

protected:

    ~CDocObjectFolder();

    LONG            _cRef;
    LPITEMIDLIST    _pidlRoot;
};

//========================================================================
// CDocObjectFolder members
//========================================================================

CDocObjectFolder::CDocObjectFolder(LPCITEMIDLIST pidlRoot)
        : _cRef(1), _pidlRoot(NULL)
{
    TraceMsg(TF_SHDLIFE, "ctor CDocObjectFolder %x", this);

    DllAddRef();

    if (pidlRoot)
        _pidlRoot = ILClone(pidlRoot);
}

CDocObjectFolder::~CDocObjectFolder()
{
    TraceMsg(TF_SHDLIFE, "dtor CDocObjectFolder %x", this);

    if (_pidlRoot)
        ILFree(_pidlRoot);

    DllRelease();
}

HRESULT CDocObjectFolder::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENTMULTI(CDocObjectFolder, IShellFolder, IShellFolder2),
        QITABENT(CDocObjectFolder, IShellFolder2),
        QITABENTMULTI(CDocObjectFolder, IPersistFolder, IPersistFolder2),
        QITABENT(CDocObjectFolder, IPersistFolder2), 
        QITABENT(CDocObjectFolder, IBrowserFrameOptions), 
        { 0 },
    };
    return QISearch(this, qit, riid, ppvObj);
}

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

ULONG CDocObjectFolder::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

HRESULT CDocObjectFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName,
        ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
{
    AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
    *ppidl = NULL;
    return E_UNEXPECTED;
}
HRESULT CDocObjectFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
    *ppenumIDList = NULL;
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut)
{
    AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
    *ppvOut = NULL;
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj)
{
    AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
    *ppvObj = NULL;
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppvOut)
{
    HRESULT hres = E_FAIL;

    if (IsEqualIID(riid, IID_IShellView))
    {
        hres = CDocObjectView_Create((IShellView**)ppvOut, this, _pidlRoot);
    }
    else
    {
        hres = E_NOINTERFACE;
        *ppvOut = NULL;
    }
    return hres;
}

HRESULT CDocObjectFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
                                 REFIID riid, UINT *prgfInOut, void **ppvOut)
{
    AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
    *ppvOut = NULL;
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
{
    //  we should never have any children.
    ASSERT(cidl == 0);
    if (cidl != 0)
        return E_UNEXPECTED;
        
    if (*rgfInOut)
    {
        //  they want to know about the document itself
        ASSERT(_pidlRoot);
        return SHGetAttributesOf(_pidlRoot, rgfInOut);
    }

    return S_OK;
}

HRESULT CDocObjectFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName)
{
    AssertMsg(FALSE, TEXT("CDocObjFolder - Called Improperly - ZekeL"));
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, 
                           DWORD uFlags, LPITEMIDLIST *ppidlOut)
{
    return E_UNEXPECTED;
}

HRESULT CDocObjectFolder::GetDefaultSearchGUID(GUID *pGuid)
{
    *pGuid = SRCID_SWebSearch;
    return S_OK;
}

HRESULT CDocObjectFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
{
    *ppenum = NULL;
    return E_NOTIMPL;
}

HRESULT CDocObjectFolder::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSID_CDocObjectFolder;
    return S_OK;
}

HRESULT CDocObjectFolder::Initialize(LPCITEMIDLIST pidl)
{
    if (_pidlRoot) 
    {
        ILFree(_pidlRoot);
        _pidlRoot = NULL;
    }

    if (pidl)
        _pidlRoot = ILClone(pidl);

    return S_OK;
}

HRESULT CDocObjectFolder::GetCurFolder(LPITEMIDLIST* ppidl)
{
    return SHILClone(_pidlRoot, ppidl);
}

// IBrowserFrameOptions
#define BASE_OPTIONS \
                            (BFO_BROWSER_PERSIST_SETTINGS | BFO_RENAME_FOLDER_OPTIONS_TOINTERNET | \
                            BFO_PREFER_IEPROCESS | BFO_ENABLE_HYPERLINK_TRACKING | \
                            BFO_USE_IE_LOGOBANDING | BFO_ADD_IE_TOCAPTIONBAR | BFO_GO_HOME_PAGE | \
                            BFO_USE_IE_TOOLBAR | BFO_NO_PARENT_FOLDER_SUPPORT | BFO_NO_REOPEN_NEXT_RESTART | \
                            BFO_SHOW_NAVIGATION_CANCELLED)


// IBrowserFrameOptions
HRESULT CDocObjectFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
{
    // We are hosting a DocObj?
    BOOL fIsFileURL = FALSE;

    // Is this under the Internet Name Space? Yes for
    // HTTP and FTP owned by the IE name space.  MSIEFTP
    // pidls are passed straight to that folder.
    // This function will return FALSE for non-IE stuff
    // but we will then want to check if it's a file system
    // thing that wants to act like a web page because it's
    // MIME TYPE or other association is associated with the web.
    if (!IsURLChild(_pidlRoot, TRUE))
    {               
        // Since IsURLChild() returned FALSE, this must be in the file system.
        // This case will happen with:
        // C:\foo.htm
        // http://www.yahoo.com/
        // http://bryanst/resume.doc
        // http://bryanst/notes.txt
        // <Start Page>  [I couldn't find a case that hit CInternetFolder]
        // C:\foo.doc (use the addressbar to repro)
        fIsFileURL = TRUE;
    }

    *pdwOptions = dwMask & BASE_OPTIONS;
    if (!fIsFileURL)
    {
        // Add the Offline Support when we aren't in the file system.
        *pdwOptions |= dwMask & (BFO_USE_IE_OFFLINE_SUPPORT | BFO_USE_DIALUP_REF);
    }
        
    return S_OK;
}


STDAPI CDocObjectFolder_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
    CDocObjectFolder *psf = new CDocObjectFolder;
    if (psf)
    {
        *ppunk = SAFECAST(psf, IShellFolder *);
        return S_OK;
    }
    return E_OUTOFMEMORY;
}


class CInternetFolder : CDocObjectFolder
{
public:
    CInternetFolder(LPCITEMIDLIST pidlRoot = NULL) ;

    // IUnknown
    STDMETHODIMP QueryInterface(REFIID, void **);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    // IShellFolder
    STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
        ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
    STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
    STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
    STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj);
    STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
    STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut);
    STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
                                 REFIID riid, UINT *prgfInOut, void **ppvOut);
    STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
    STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, 
                           DWORD uFlags, LPITEMIDLIST * ppidlOut);

    // IPersistFolder
    STDMETHODIMP GetClassID(CLSID *pClassID);

    // IBrowserFrameOptions
    STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions);

protected:
    ~CInternetFolder();

    HRESULT _CreateProtocolHandler(LPCSTR pszProtocol, IBindCtx * pbc, IShellFolder **ppsf);
    HRESULT _CreateProtocolHandlerFromPidl(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsf);
    HRESULT _GetAttributesOfProtocol(LPCSTR pszProtocol, LPCITEMIDLIST *apidl, UINT cpidl, ULONG *rgfInOut);
    HRESULT _FaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IUnknown * punkSite);
    HRESULT _ConditionallyFaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IBindCtx * pbc);
    HRESULT _AssocCreate(LPCITEMIDLIST pidl, REFIID riid, void * *ppv);
    HRESULT _GetScheme(LPCITEMIDLIST pidl, LPWSTR pszOut, DWORD cchOut);
    HRESULT _GetUIObjectFromShortcut(LPCITEMIDLIST pidl, REFIID riid, void **ppvOut);
    HRESULT _GetTitle(LPCWSTR pszUrl, STRRET *pstr);
    HRESULT _InitHistoryStg(IUrlHistoryStg **pphist);

    IUrlHistoryStg *_phist;
};


CInternetFolder::CInternetFolder(LPCITEMIDLIST pidlRoot)
    : CDocObjectFolder(pidlRoot)
{
    TraceMsg(TF_URLNAMESPACE, "[%X] ctor CInternetFolder", this);
    ASSERT(NULL == _phist);
}

CInternetFolder::~CInternetFolder()
{
    ATOMICRELEASE(_phist);
    TraceMsg(TF_URLNAMESPACE, "[%X] dtor CInternetFolder", this);
}

HRESULT CInternetFolder::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENTMULTI(CInternetFolder, IShellFolder, IShellFolder2),
        QITABENT(CInternetFolder, IShellFolder2),
        QITABENTMULTI(CDocObjectFolder, IPersistFolder, IPersistFolder2),
        QITABENT(CDocObjectFolder, IPersistFolder2), 
        QITABENT(CInternetFolder, IBrowserFrameOptions), 
        { 0 },
    };
    return QISearch(this, qit, riid, ppvObj);
}

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

ULONG CInternetFolder::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}


typedef struct tagURLID 
{
    ITEMIDLIST idl;     //  cb and SHID
    BYTE bType;         //  URLID
    UINT uiCP;          //  Code Page
    WCHAR achUrl[1];       //  variable size string
} URLID;

#define SHID_INTERNET           0x60
#define SHID_INTERNET_SITE      0x61    // IE name space item

#define URLID_URLBASEA          0x00
/////// URLID_LOCATION          0x01  //  LEGACY IE3/4 used for Frag IDs
/////// URLID_FTPFOLDER         0x02  //  LEGACY used by a pre-release FTP Folder dll
#define URLID_PROTOCOL          0x03  //  this is actually a delegated protocol
#define URLID_URLBASEW          0x80  //  
//      URLIDF_UNICODE          0x80  //  URLID_ is actually of UNICODE type

#ifdef UNICODE 
#define URLID_URLBASE           URLID_URLBASEW
#else
#define URLID_URLBASE           URLID_URLBASEA
#endif

typedef const UNALIGNED URLID *PCURLID;
typedef UNALIGNED URLID *PURLID;

#define PDID_SIG MAKEWORD(SHID_INTERNET_SITE, URLID_PROTOCOL)

inline PCDELEGATEITEMID _IsValidDelegateID(LPCITEMIDLIST pidl)
{
    PCDELEGATEITEMID pdi = (PCDELEGATEITEMID)pidl;
    ASSERT(pdi);

    if ((pdi->cbSize >= (SIZEOF(PDELEGATEITEMID)-1))
    && (pdi->wOuter == PDID_SIG))
        return pdi;

    return NULL;
}
    
LPCSTR _PidlToDelegateProtocol(LPCITEMIDLIST pidl)
{
    PCDELEGATEITEMID pdi = _IsValidDelegateID(pidl);
    if (pdi)
        return (LPCSTR)&(pdi->rgb[pdi->cbInner]);

    return NULL;
}

inline PCURLID _IsValidUrlID(LPCITEMIDLIST pidl)
{
    PCURLID purlid = (PCURLID)pidl;
    ASSERT(purlid);

//  98/12/22 #263932 vtan: ANSI and Unicode URLs are both valid. Use function
//  _ExtractURL to extract the URL from the PIDL as a Unicode string.

    if (purlid->idl.mkid.cb >= SIZEOF(URLID)
    && (purlid->idl.mkid.abID[0] == SHID_INTERNET_SITE)
    && (purlid->bType == URLID_URLBASEA || purlid->bType == URLID_URLBASEW || _IsValidDelegateID(pidl)))
        return purlid;

    return NULL;
}

//  98/12/22 #263932 vtan: IE4 stores the PIDL in a stream as an ANSI
//  string. IE5 stores the PIDL in a stream as a Unicode string. This
//  functions reads the string (ANSI or Unicode) and converts it to
//  an internal Unicode string which is what will be written to the stream.

void _ExtractURL (PCURLID pURLID, LPWSTR wszURL, int iCharCount)
{
    if (pURLID->bType == URLID_URLBASEA)
    {
        char aszURL[MAX_URL_STRING];

#ifdef UNIX
        ualstrcpynA(aszURL, (const char*)(pURLID->achUrl), sizeof(aszURL));
#else
        ualstrcpynA(aszURL, reinterpret_cast<const char*>(pURLID->achUrl), sizeof(aszURL));
#endif
        SHAnsiToUnicode(aszURL, wszURL, iCharCount);
    }
    else if (pURLID->bType == URLID_URLBASEW)
    {
        ualstrcpynW(wszURL, pURLID->achUrl, iCharCount);
    }
}

//  99/01/04 vtan: Added the following to help compare URLIDs which
//  can be AA/UU/AU/UA and perform the correct comparison.

int _CompareURL (PCURLID pURLID1, PCURLID pURLID2)
{
    int iResult;
    
    if ((pURLID1->bType == URLID_URLBASEA) && (pURLID2->bType == URLID_URLBASEA))
    {
#ifdef UNIX
        iResult = ualstrcmpA((const char*)(pURLID1->achUrl), (const char*)(pURLID2->achUrl));
#else
        iResult = ualstrcmpA(reinterpret_cast<const char*>(pURLID1->achUrl), reinterpret_cast<const char*>(pURLID2->achUrl));
#endif
    }
    else if ((pURLID1->bType == URLID_URLBASEW) && (pURLID2->bType == URLID_URLBASEW))
    {
        iResult = ualstrcmpW(pURLID1->achUrl, pURLID2->achUrl);
    }
    else
    {
        PCURLID pCompareURLID;
        WCHAR wszURL[MAX_URL_STRING];
        
        //  AU/UA comparison. To be efficient only convert the ANSI URLID
        //  to Unicode and perform the comparison in Unicode.
        
        if (pURLID1->bType == URLID_URLBASEA)
        {
            pCompareURLID = pURLID2;
            _ExtractURL(pURLID1, wszURL, SIZECHARS(wszURL));
        }
        else
        {
            pCompareURLID = pURLID1;
            _ExtractURL(pURLID2, wszURL, SIZECHARS(wszURL));
        }
        iResult = ualstrcmpW(pCompareURLID->achUrl, wszURL);
    }
    return iResult;
}

IShellFolder* g_psfInternet = NULL;

STDAPI CDelegateMalloc_Create(void *pv, UINT cbSize, WORD wOuter, IMalloc **ppmalloc);
//
// this might modify pszName if it's not a fully qualified url!
BOOL _ValidateURL(LPTSTR pszName, DWORD dwFlags)
{
    //
    // WARNING: In order to allow URL extensions, we assume all strings
    //  which contains ":" in it is a valid string.
    // Assumptions are:
    //
    // (1) CDesktop::ParseDisplayName parse file system strings first.
    // (2) URL moniker will return an error correctly if URL is not valid.
    // (3) someone else (the desktop folder) handles shell: URLs
    //     they should not be used directly by the browser 
    //
    return SUCCEEDED(IURLQualify(pszName, dwFlags, pszName, NULL, NULL)) && (-1 != GetUrlScheme(pszName));
    HRESULT hr = IURLQualify(pszName, dwFlags, pszName, NULL, NULL);
    DWORD nScheme = GetUrlScheme(pszName);
    return SUCCEEDED(hr) && (-1 != nScheme) && (URL_SCHEME_SHELL != nScheme);
}

LPITEMIDLIST IEILAppendFragment(LPITEMIDLIST pidl, LPCWSTR pszFragment)
{
    // WARNING: See IE5 bug #'s 86951 and 36497 for more details.
    //         In a nutshell, we're rolling back the change for 36497 because
    //         the change caused many more problems with customers than
    //         the behavior we had in IE4.
    // 
    //         Because we're not ensuring that
    //         the fragment is prefixed with a '#', there may be
    //         cases where the URL in the address bar looks wrong,
    //         as well as cases where a hyperlink to a different doc
    //         or HTML page may fail if it contains a fragment.
    return ILAppendHiddenStringW(pidl, IDLHID_URLFRAGMENT, pszFragment);
}

// browser only uglyness... we need to construct a desktop relative "regitem" pidl for
// the internet since browser only shell does not support psf->ParseDisplayName("::{guid}", &pidl)
// this uses the same layout as REGITEMs so we have PIDL compatibility with integrated mode
// this ensures that a shortcut to the IE icon made in browser only mode works in integrated

#ifndef NOPRAGMAS
#pragma pack(1)
#endif
typedef struct
{
    WORD    cb;
    BYTE    bFlags;
    BYTE    bReserved;  // This is to get DWORD alignment
    CLSID   clsid;
} IDITEM;               // IDREGITEM

typedef struct
{
    IDITEM idri;
    USHORT cbNext;
} IDLITEM;              // IDLREGITEM
#ifndef NOPRAGMAS
#pragma pack()
#endif

// stolen from shell32\shitemid.h

#define SHID_ROOT_REGITEM       0x1f    // MyDocuments, Internet, etc

const IDLITEM c_idlInetRoot = 
{ 
    {SIZEOF(IDITEM), SHID_ROOT_REGITEM, 0, 
    { 0x871C5380, 0x42A0, 0x1069, 0xA2,0xEA,0x08,0x00,0x2B,0x30,0x30,0x9D },/* CLSID_Internet */ }, 0,
};

LPCITEMIDLIST c_pidlURLRoot = (LPCITEMIDLIST)&c_idlInetRoot;

// it must be an absolute pidl with a root regitem id at the front
// if we're a rooted explorer, this is always false
// this means we're definitely in nashville, so we shouldn't have a split
// world

PCURLID _FindUrlChild(LPCITEMIDLIST pidl, BOOL fIncludeHome = FALSE)
{
    if ((pidl == NULL) ||
        (pidl->mkid.cb != sizeof(IDITEM)) ||
        (pidl->mkid.abID[0] != SHID_ROOT_REGITEM))
    {
        return NULL;
    }

    //
    // the clsid in the pidl must be our internet folder's
    //
    if (!IsEqualGUID(((IDITEM*)pidl)->clsid, CLSID_Internet))
    {
        ASSERT(!IsEqualGUID(((IDITEM*)pidl)->clsid, CLSID_CURLFolder));
        return NULL;
    }

    //  go to the child...
    pidl = _ILNext(pidl);
    
    //
    // if it is a pidl to the internet root then it is the IE3 Home Page
    //
    
    if (fIncludeHome && ILIsEmpty(pidl))
        return (PCURLID)pidl;

    //
    // otherwise it is our child if it is a site object
    //
    return _IsValidUrlID(pidl);
}

STDAPI_(BOOL) IsURLChild(LPCITEMIDLIST pidl, BOOL fIncludeHome)
{
    return (NULL != _FindUrlChild(pidl, fIncludeHome));
}


BOOL IEILGetFragment(LPCITEMIDLIST pidl, LPWSTR pszFragment, DWORD cchFragment)
{
    return ILGetHiddenStringW(pidl, IDLHID_URLFRAGMENT, pszFragment, cchFragment);
}

UINT IEILGetCP(LPCITEMIDLIST pidl)
{
    PCURLID purlid = _FindUrlChild((pidl));
    if (purlid)
    {
        if (!_IsValidDelegateID((LPCITEMIDLIST)purlid))
            return purlid->uiCP;
    }
    return CP_ACP;
}

LPITEMIDLIST _UrlIdCreate(UINT uiCP, LPCTSTR pszUrl)
{
    //
    //  the URLID has a variable sized string
    //  member.  but we put the arbitrary limit
    //  of MAX_URL_STRING because that is what
    //  we use everywhere else.  we could just remove the
    //  limit however.
    //
    USHORT cb = (USHORT)SIZEOF(URLID) - (USHORT)CbFromCch(1);
    USHORT cchUrl = lstrlen(pszUrl) + 1;
    cchUrl = (USHORT)min(cchUrl, MAX_URL_STRING);
    cb += CbFromCch(cchUrl);

    PURLID purlid = (PURLID)IEILCreate(cb + SIZEOF(USHORT));

    if (purlid)
    {
        //  everything is actually aligned right now...
        purlid->idl.mkid.cb = cb;
        purlid->idl.mkid.abID[0] = SHID_INTERNET_SITE;
        purlid->bType = URLID_URLBASE;
        purlid->uiCP = uiCP;
        ualstrcpyn(purlid->achUrl, pszUrl, cchUrl);
    }

    return (LPITEMIDLIST) purlid;
}
        
LPITEMIDLIST UrlToPidl(UINT uiCP, LPCTSTR pszUrl, LPCWSTR pszFragment)
{
    LPITEMIDLIST pidlRet;
    LPCTSTR pszAttachedFrag = UrlGetLocation(pszUrl);
    TCHAR szURLBuf[MAX_URL_STRING];

    //  deal with URL's that still include the location (as in ParseDisplayName)
    if (pszAttachedFrag) 
    {
        StrCpyN(szURLBuf, pszUrl, (int)(pszAttachedFrag-pszUrl+1));
        pszUrl = szURLBuf;

        //  prefer the passed in fragment to the attached one
        if (!pszFragment)
            pszFragment = pszAttachedFrag;
            
    }

    ASSERT(pszUrl);
    
    pidlRet = _UrlIdCreate(uiCP, pszUrl);

    if (pidlRet && pszFragment && *pszFragment)
        pidlRet = IEILAppendFragment(pidlRet, pszFragment);

    return pidlRet;
}

typedef struct
{
    LPCSTR pszProtocol;
    const CLSID * pCLSID;
} FAULTIN_URLHANDERS;

// TODO: If there are other URL Handlers, add them here.
const FAULTIN_URLHANDERS c_FaultInUrlHandlers[] =
{
    {"ftp", &CLSID_FTPShellExtension}
};

HRESULT CInternetFolder::_FaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IUnknown * punkSite)
{
    HRESULT hr = S_OK;
    if (pszProtocol)
    {
        for (int nIndex = 0; nIndex < ARRAYSIZE(c_FaultInUrlHandlers); nIndex++)
        {
            if (!StrCmpIA(pszProtocol, c_FaultInUrlHandlers[nIndex].pszProtocol))
            {
                // Only fault in the feature if we are navigating to an FTP directory.
                if ((0 == nIndex) && !UrlIs(pszUrl, URLIS_DIRECTORY))
                {
                    // It's not an ftp directory, so skip it.
                    continue;
                }

                // FTP has a URL Shell Extension handler that is optionally
                // installed.  Fault it in now if it's needed.
                uCLSSPEC ucs;
                QUERYCONTEXT qc = { 0 };
                HWND hwnd = NULL;

                ucs.tyspec = TYSPEC_CLSID;
                ucs.tagged_union.clsid = *c_FaultInUrlHandlers[nIndex].pCLSID;

                IUnknown_GetWindow(punkSite, &hwnd);
                if (EVAL(hwnd))
                {
                    // Make it modal while the dialog is being displayed.
                    IUnknown_EnableModless(punkSite, FALSE);
                    FaultInIEFeature(hwnd, &ucs, &qc, 0);
                    IUnknown_EnableModless(punkSite, TRUE);
                }
                break;    // pidl can only have 1 procotol, so we don't need to check the other protocol.
            }
        }
    }

    return hr;    // We don't care if it didn't make it.
}


HRESULT CInternetFolder::_ConditionallyFaultInUrlHandler(LPCSTR pszProtocol, LPCTSTR pszUrl, IBindCtx * pbc)
{
    HRESULT hr = S_OK;

    // Faulting in the feature will probably require UI, so we need to assure that the caller
    // will allow this.
    if (pbc)
    {
        IUnknown * punkSite = NULL;

        pbc->GetObjectParam(STR_DISPLAY_UI_DURING_BINDING, &punkSite);
        if (punkSite)
        {
            hr = _FaultInUrlHandler(pszProtocol, pszUrl, punkSite);
            punkSite->Release();
        }
    }

    ASSERT(SUCCEEDED(hr));
    return S_OK;    // We don't care if it didn't make it.
}


// returns:
//      success S_OK
//      failure FAILED(hres)

HRESULT CInternetFolder::_CreateProtocolHandler(LPCSTR pszProtocol, IBindCtx * pbc, IShellFolder **ppsf)
{
    HRESULT hres;
    CHAR szCLSID[GUIDSTR_MAX];
    DWORD cbSize = SIZEOF(szCLSID);

    *ppsf = NULL;

    if (pszProtocol && 
        SHGetValueA(HKEY_CLASSES_ROOT, pszProtocol, "ShellFolder", NULL, &szCLSID, &cbSize) == ERROR_SUCCESS)
    {
        CLSID clsid;
        IShellFolder *psf;

        GUIDFromStringA(szCLSID, &clsid);
        if (!SHSkipJunction(pbc, &clsid))
        {
            hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psf));
            if (SUCCEEDED(hres))
            {
                // IPersistFolder is optional
                IPersistFolder *ppf;
                if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf))))
                {
                    ppf->Initialize(_pidlRoot);
                    ppf->Release();
                }

                IDelegateFolder *pdf;
                hres = psf->QueryInterface(IID_PPV_ARG(IDelegateFolder, &pdf));
                if (SUCCEEDED(hres))
                {
                    // REVIEW: we could cache the malloc on a per protocol basis
                    // to avoid creating these over and over
                    IMalloc *pmalloc;
                    hres = CDelegateMalloc_Create((void*)pszProtocol, (lstrlenA(pszProtocol) + 1), PDID_SIG, &pmalloc);
                    if (SUCCEEDED(hres))
                    {
                        hres = pdf->SetItemAlloc(pmalloc);
                        pmalloc->Release();
                    }
                    pdf->Release();
                }

                if (SUCCEEDED(hres))
                {
                    hres = S_OK;    // force all success codes to S_OK 
                    *ppsf = psf;
                }
                else
                    psf->Release();
            }
        }
        else
            hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
    }
    else
        hres = E_FAIL;

    return hres;
}

// returns:
//      S_FALSE if it is not a delegate protocol PIDL
//      hres of the bind opteration to the delegate protocol handler

HRESULT CInternetFolder::_CreateProtocolHandlerFromPidl(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsf)
{
    LPCSTR pszProtocol = _PidlToDelegateProtocol(pidl);
    if (pszProtocol)
    {
        HRESULT hres = _CreateProtocolHandler(pszProtocol, pbc, ppsf);
        ASSERT(hres != S_FALSE);    // enforce the return value comment
        return hres;
    }

    *ppsf = NULL;
    return S_FALSE;     // not a protocal PIDL
}

BOOL _GetUrlProtocol(LPCTSTR pszUrl, LPSTR pszProtocol, DWORD cchProtocol)
{
    TCHAR sz[MAX_PATH];
    DWORD cch = SIZECHARS(sz);
    if (SUCCEEDED(UrlGetPart(pszUrl, sz, &cch, URL_PART_SCHEME, 0)))
    {
        SHTCharToAnsi(sz, pszProtocol, cchProtocol);
        return TRUE;
    }

    return FALSE;
}

UINT CodePageFromBindCtx(LPBC pbc)
{
    UINT uiCP = CP_ACP;
    IDwnCodePage *pDwnCP;
    if (pbc && SUCCEEDED(pbc->QueryInterface(IID_PPV_ARG(IDwnCodePage, &pDwnCP))))
    {
        uiCP = pDwnCP->GetCodePage();
        pDwnCP->Release();
    }
    return uiCP;
}

#define STR_PARSE_INTERNET_DONT_ESCAPE_SPACES   L"Parse Internet Dont Escape Spaces"

HRESULT CInternetFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName,
        ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
{
    HRESULT hres = E_FAIL;

    TCHAR szName[MAX_URL_STRING];
    UINT uiCP = CodePageFromBindCtx(pbc);

    StrCpyN(szName, pwszDisplayName, ARRAYSIZE(szName));
    if (!PathIsFilePath(szName))
    {
        if (_ValidateURL(szName, 0) || ShouldShellExecURL(szName))
        {
            CHAR szProtocol[MAX_PATH];
            DWORD cchName = ARRAYSIZE(szName);
            IShellFolder *psfHandler;
            BOOL fProtocolExists;

            // if we're down here, then the szName was really a url so try to encode it.
            // turn spaces to %20, unless we are being called from shellexec
            // in which case we allow spaces in the URL
            if (!BindCtx_ContainsObject(pbc, STR_PARSE_INTERNET_DONT_ESCAPE_SPACES))
                UrlEscape(szName, szName, &cchName, URL_ESCAPE_SPACES_ONLY);

            fProtocolExists = _GetUrlProtocol(szName, szProtocol, ARRAYSIZE(szProtocol));
            _ConditionallyFaultInUrlHandler(szProtocol, szName, pbc);

            if (fProtocolExists &&
                _CreateProtocolHandler(szProtocol, pbc, &psfHandler) == S_OK)
            {
                TraceMsg(TF_PIDLWRAP, "Asking \"%s\" handler to parse %s (%08X) into a pidl", szProtocol, szName, szName);
                hres = psfHandler->ParseDisplayName(hwnd, pbc,
                                                    pwszDisplayName, pchEaten,
                                                    ppidl, pdwAttributes);
                TraceMsg(TF_PIDLWRAP, "the result is %08X, the pidl is %08X", hres, *ppidl);
                psfHandler->Release();
                TraceMsg(TF_URLNAMESPACE, "CODF::PDN(%s) called psfHandler and returning %x",
                         szName, hres);
            }
            else
            {
                *ppidl = UrlToPidl(uiCP, szName, NULL);
                if (*ppidl)
                {
                    if (pdwAttributes)
                        hres = _GetAttributesOfProtocol(NULL, (LPCITEMIDLIST *)ppidl, 1, pdwAttributes);
                    else
                        hres = S_OK;
                }
                else 
                    hres = E_OUTOFMEMORY;

                TraceMsg(TF_URLNAMESPACE, "CODF::PDN(%s) called UrlToPidl and returning %x", szName, hres);
            }
        } 
        else 
        {
            TraceMsg(DM_CDOFPDN, "CDOF::PDN(%s) returning E_FAIL because of (%s) is FALSE", szName, TEXT("(_ValidateURL(szName) || ShouldShellExecURL( szName ))"));
        }
    } 

    return hres;
}

class CInternetFolderDummyEnum : public IEnumIDList
{
public:
    CInternetFolderDummyEnum();
    // *** IUnknown methods ***
    STDMETHODIMP QueryInterface(REFIID,void **);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    // *** IEnumIDList methods ***
    STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
    STDMETHODIMP Skip(ULONG celt) {return E_NOTIMPL;}
    STDMETHODIMP Reset(void){return E_NOTIMPL;}
    STDMETHODIMP Clone(LPENUMIDLIST *ppenum){return E_NOTIMPL;}

protected:
    ~CInternetFolderDummyEnum() {;}
    
    long _cRef;
};

CInternetFolderDummyEnum::CInternetFolderDummyEnum() : _cRef(1)
{
}

HRESULT CInternetFolderDummyEnum::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CInternetFolderDummyEnum, IEnumIDList),
        { 0 },
    };
    return QISearch(this, qit, riid, ppvObj);
}

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

ULONG CInternetFolderDummyEnum::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;

    delete this;
    return 0;
}

HRESULT CInternetFolderDummyEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
{
    pceltFetched = 0;
    return S_FALSE;
}

HRESULT CInternetFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
    CInternetFolderDummyEnum *pdummy = new CInternetFolderDummyEnum();

    if (pdummy)
    {
        *ppenumIDList = (IEnumIDList *)pdummy;
        return S_OK;
    }
    return E_OUTOFMEMORY;
}

HRESULT CInternetFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut)
{
    IShellFolder *psfHandler = NULL;
    BOOL fUseDefault = TRUE;
    *ppvOut = NULL;

    if (!_IsValidUrlID(pidl))
        return E_INVALIDARG;
        
    HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, pbc, &psfHandler);
    if (hres == S_OK)
    {
        // NOTE: We allow Shell Extensions to take over URL handling on a per
        //     URL basis.  We entered the _CreateProtocolHandlerFromPidl()
        //     block of code above because
        //     a shell extension is registered to take over handling  this
        //     URL.  The above call to IShellFolder::BindToObject() just failed,
        //     so we need to fall back and handle it in the traditional way.
        //     This can be used by Shell Extensions, like the Ftp ShellExt, to
        //     let the browser (us) handle URLs that are either inaccessible because of
        //     the proxy or allow the browser to handle it so the traditional code
        //     will: 1) download the item(s), 2) sniff the data for the type, 3)
        //     use the suggested MIME type from the server or in the web page, 4)
        //     check the file for type extension mappings, 5)
        //     check any downloaded file for security certificates, and 6) display
        //     Open/Save dialogs.
            
        hres = psfHandler->BindToObject(pidl, pbc, riid, ppvOut);

        //  the handler will return ERROR_CANCELLED if it wants default behavior
        if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hres)
            fUseDefault = FALSE;
    }

    if (fUseDefault)
    {
        STRRET strRet;

        if (psfHandler)
        {
            //  we had a delegated folder that failed, need a normal pidl
            hres = psfHandler->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet);
        }
        else
            hres = GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strRet);

        TCHAR szUrl[MAX_URL_STRING];
        if (SUCCEEDED(hres) &&
            SUCCEEDED(hres = StrRetToBuf(&strRet, pidl, szUrl, ARRAYSIZE(szUrl))))
        {
            if (IsEqualIID(IID_IMoniker, riid))
            {
                hres = MonikerFromURL(szUrl, (IMoniker **)ppvOut);
            }
            else // DEFAULT
            {
                //  create a ShellFolder for the caller
                hres = E_OUTOFMEMORY;
                LPITEMIDLIST pidlT = NULL;

                //  if we are using a handler but it returned cancelled,
                //  then we need to recreate the pidl for ourselves to use
                //  otherwise we just use the one that was passed in, 
                //  which we assume was the one we created.
                if (psfHandler)
                {
                    pidlT = UrlToPidl(CP_ACP, szUrl, NULL);
                    pidl = pidlT;
                }

                if (pidl)
                {
                    LPITEMIDLIST pidlFull = ILCombine(_pidlRoot, pidl);

                    if (pidlFull)
                    {
                        CDocObjectFolder *psf = new CDocObjectFolder(pidlFull);
                        if (psf)
                        {
                            hres = psf->QueryInterface(riid, ppvOut);
                            psf->Release();
                        }
                        
                        ILFree(pidlFull);
                    }

                    ILFree(pidlT);
                }
            }
        }
    }

    
    if (psfHandler)
        psfHandler->Release();
        
    return hres;
}

HRESULT CInternetFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvObj)
{
    IShellFolder *psfHandler;

    *ppvObj = NULL;

    if (!_IsValidUrlID(pidl))
        return E_INVALIDARG;

    HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, pbc, &psfHandler);
    if (hres != S_FALSE)
    {
        if (SUCCEEDED(hres))
        {
            hres = psfHandler->BindToStorage(pidl, pbc, riid, ppvObj);
            psfHandler->Release();
        }
        return hres;
    }
    *ppvObj = NULL;
    return E_NOINTERFACE;
}

int CALLBACK CompareDelegateProtocols(void *pv1, void *pv2, LPARAM lParam)
{
    LPCSTR psz1 = _PidlToDelegateProtocol((LPCITEMIDLIST)pv1);
    LPCSTR psz2 = _PidlToDelegateProtocol((LPCITEMIDLIST)pv2);

    if (psz1 && psz2)
    {
        int iRet = StrCmpA(psz1, psz2);
        if (0 == iRet && lParam)
            *((LPCSTR *)lParam) = psz1;
    }
    else if (psz1)
    {
        return 1;
    }
    else if (psz2)
    {
        return -1;
    }
    return 0;
}


HRESULT CInternetFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    int iRet;

    ASSERT(!ILIsEmpty(pidl1) && !ILIsEmpty(pidl2));

    // Check for protocol pidls.
    LPCSTR psz = NULL;
    iRet = CompareDelegateProtocols((void *)pidl1, (void *)pidl2, (LPARAM)&psz);
    if (iRet)
        return ResultFromShort(iRet);

    if (psz)
    {
        IShellFolder *psfHandler;
        if (_CreateProtocolHandler(psz, NULL, &psfHandler) == S_OK)
        {
            iRet = psfHandler->CompareIDs(lParam, pidl1, pidl2);
            psfHandler->Release();
            return ResultFromShort(iRet);
        }
        
    }

    //  we only have one layer of children
    ASSERT(ILIsEmpty(_ILNext(pidl1)));
    ASSERT(ILIsEmpty(_ILNext(pidl2)));

    PCURLID purlid1 = _IsValidUrlID(pidl1);

    if (purlid1)
    {
        PCURLID purlid2 = _IsValidUrlID(pidl2);

        if (purlid2)
        {
            iRet = _CompareURL(purlid1, purlid2);
        }
        else
        {
            iRet = -1;
        }
    }
    else
    {
        iRet = 1;
    }

    return ResultFromShort(iRet);
}


HRESULT CInternetFolder::_GetAttributesOfProtocol(LPCSTR pszProtocol,
                                                   LPCITEMIDLIST *apidl,
                                                   UINT cpidl, ULONG *rgfInOut)
{
    HRESULT hres;

    ASSERT(cpidl);
    
    if (pszProtocol)
    {
        //
        // We have a protocol.  Find the protocol handler
        // and pass it the bundle of pidls.
        //
        IShellFolder *psfHandler;
        hres = _CreateProtocolHandler(pszProtocol, NULL, &psfHandler);
        if (hres == S_OK)
        {
            hres = psfHandler->GetAttributesOf(cpidl, apidl, rgfInOut);
            psfHandler->Release();
        }
    }
    else if (_IsValidUrlID(apidl[0]))
    {
        ULONG uOut = SFGAO_CANLINK | SFGAO_BROWSABLE | SFGAO_STREAM;
        *rgfInOut &= uOut;
        hres = S_OK;
        
    }
    else
        hres = E_INVALIDARG;

    return hres;
}


HRESULT CInternetFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
{
    if (*rgfInOut)
    {
        //
        // Internet folder case.
        //
        LPCSTR pszProtocol;

        if (cidl == 0)
        {
            //
            // They are asking about the Internet Folder itself.
            //
            *rgfInOut &= SFGAO_FOLDER | SFGAO_CANLINK | SFGAO_STREAM;
        }
        else if (cidl == 1)
        {
            //
            // Often we are asked about only one child,
            // so we optimize that case.
            //
            pszProtocol = _PidlToDelegateProtocol(apidl[0]);

            _GetAttributesOfProtocol(pszProtocol, apidl, cidl, rgfInOut);
        }
        else
        {
            //
            // They are asking about multiple internet children.
            // These children may have different protocols,
            // so we have to find the GetAttributesOf handler for
            // each group of protocols in the list.
            //
            LPCITEMIDLIST pidlBase;
            UINT i, cpidlGroup;

            // Create a list of pidls sorted by protocol.
            HDPA hdpa = DPA_Create(100);
            if (!hdpa)
                return E_OUTOFMEMORY;

            for (i = 0; i < cidl; i++)
            {
                DPA_AppendPtr(hdpa, (void *)apidl[i]);
            }
            DPA_Sort(hdpa, CompareDelegateProtocols, NULL);

            //
            // Call GetAttributesOf on each protocol group.
            // A group
            //   starts at pidlBase
            //   contains cpidlGroup pidls
            //   has a protocol of pszProtocol
            //
            pidlBase = (LPCITEMIDLIST)DPA_FastGetPtr(hdpa, 0);
            pszProtocol = NULL;
            cpidlGroup = 0;
            for (i = 0; *rgfInOut && (i < cidl); i++)
            {
                LPCITEMIDLIST pidlNew = (LPCITEMIDLIST)DPA_FastGetPtr(hdpa, i);
                LPCSTR pszProtocolNew = _PidlToDelegateProtocol(pidlNew);
                if (pszProtocolNew)
                {
                    // See if we have a new protocol.
                    if (!pszProtocol || StrCmpA(pszProtocol, pszProtocolNew))
                    {
                        // We have a new protocol, time to process
                        // the last batch pidls.
                        _GetAttributesOfProtocol(pszProtocol, &pidlBase, cpidlGroup, rgfInOut);

                        pidlBase = pidlNew;
                        pszProtocol = pszProtocolNew;
                        cpidlGroup = 0;
                    }
                }
                cpidlGroup++;
            }
            if (*rgfInOut)
            {
                ASSERT(cpidlGroup);
                _GetAttributesOfProtocol(pszProtocol, &pidlBase, cpidlGroup, rgfInOut);
            }

            DPA_Destroy(hdpa);
            hdpa = NULL;
        }
    }

    return S_OK;
}

BOOL GetCommonProtocol(LPCITEMIDLIST *apidl, UINT cpidl, LPCSTR *ppszProtocol)
{
    UINT ipidl;
    LPCSTR pszProtocol;
    LPCSTR pszProtocolNext;

    *ppszProtocol = NULL;

    if (cpidl == 0)
    {
        return TRUE;    // No pidls - no protocols, but they do all match!
    }

    //
    // Grab the protocol of the first pidl, and use it to compare
    // against the rest of the pidls.
    //
    pszProtocol = _PidlToDelegateProtocol(apidl[0]);

    for (ipidl=1; ipidl<cpidl; ipidl++)
    {

        pszProtocolNext = _PidlToDelegateProtocol(apidl[ipidl]);

        //
        // Check if the protocols are different.
        //
        if ((pszProtocol != pszProtocolNext) &&
            ((pszProtocol == NULL) ||
             (pszProtocolNext == NULL) ||
             (StrCmpA(pszProtocol, pszProtocolNext) != 0)))
        {
            return FALSE;
        }
    }

    *ppszProtocol = pszProtocol;
    return TRUE;
}

HRESULT _CombineHidden(LPCITEMIDLIST pidl, DWORD dwIEFlags, LPWSTR pszName, DWORD cchName)
{
    HRESULT hres = S_OK;
    // 
    //  need to correctly append the fragment and query to the base
    //  if pszName is a DOSPATH, it will be converted to a
    //  file: URL so that it can accomadate the location
    //
    WCHAR sz[MAX_URL_STRING];
    DWORD cch = cchName;

    if (ILGetHiddenStringW(pidl, IDLHID_URLQUERY, sz, SIZECHARS(sz)))
        hres = UrlCombineW(pszName, sz, pszName, &cch, 0);
    
    if (!(dwIEFlags & IEGDN_NOFRAGMENT) && IEILGetFragment(pidl, sz, SIZECHARS(sz)))
    {
        hres = UrlCombineW(pszName, sz, pszName, &cchName, 0);
    }

    //  else 
    //      BUBBUG - should we return just the fragment in some case?
    return hres;
}

HRESULT CInternetFolder::_GetUIObjectFromShortcut(LPCITEMIDLIST pidl, REFIID riid, void **ppvOut)
{
    HRESULT hres = E_NOINTERFACE;
    STRRET str;
    TCHAR sz[MAX_URL_STRING];

    if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)) 
    && SUCCEEDED(StrRetToBuf(&str, pidl, sz, ARRAYSIZE(sz)))
    && SUCCEEDED(_CombineHidden(pidl, 0, sz, ARRAYSIZE(sz))))
    {
        IUniformResourceLocator *purl;
        hres = CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_INPROC_SERVER,
                IID_PPV_ARG(IUniformResourceLocator, &purl));
        if (SUCCEEDED(hres))
        {
            hres = purl->SetURL(sz, 0);
            
            if (SUCCEEDED(hres))
            {
                IShellLink * psl;
                if (SUCCEEDED(purl->QueryInterface(IID_PPV_ARG(IShellLink, &psl))))
                {
                    if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_INFOLDER, &str)) &&
                        SUCCEEDED(StrRetToBuf(&str, pidl, sz, ARRAYSIZE(sz))))
                    {
                        PathRenameExtension(sz, TEXT(".url"));
                        psl->SetDescription(sz);
                    }
                    psl->Release();
                }
                
                hres = purl->QueryInterface(riid, ppvOut);
            }
            purl->Release();
        }
    }       

    return hres;
}

HRESULT CInternetFolder::_GetScheme(LPCITEMIDLIST pidl, LPWSTR pszOut, DWORD cchOut)
{
    STRRET str;
    LPCSTR pszProtocol = _PidlToDelegateProtocol(pidl);

    if (pszProtocol)
    {
        SHAnsiToUnicode(pszProtocol, pszOut, cchOut);
        return S_OK;
    }
    else if (SUCCEEDED(GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str)))
    {
        WCHAR sz[MAX_URL_STRING];
        if (SUCCEEDED(StrRetToBufW(&str, pidl, sz, ARRAYSIZE(sz))))
        {
            return UrlGetPartW(sz, pszOut, &cchOut, URL_PART_SCHEME, 0);
        }
    }
    return E_FAIL;
}
    
HRESULT CInternetFolder::_AssocCreate(LPCITEMIDLIST pidl, REFIID riid, void * *ppv)
{
    *ppv = NULL;

    IQueryAssociations *pqa;
    HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
    if (SUCCEEDED(hr))
    {
        WCHAR szScheme[MAX_PATH];
        _GetScheme(pidl, szScheme, SIZECHARS(szScheme));

        hr = pqa->Init(0, szScheme, NULL, NULL);

        if (SUCCEEDED(hr))
            hr = pqa->QueryInterface(riid, ppv);

        pqa->Release();
    }

    return hr;
}

HRESULT CInternetFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
                                 REFIID riid, UINT *prgfInOut, void **ppvOut)
{
    HRESULT hres = E_NOINTERFACE;
    LPCSTR pszProtocol;
    
    *ppvOut = NULL;

    if (apidl[0] && GetCommonProtocol(apidl, cidl, &pszProtocol) && pszProtocol)
    {
        IShellFolder *psfHandler;
        hres = _CreateProtocolHandlerFromPidl(apidl[0], NULL, &psfHandler);
        if (hres != S_FALSE)
        {
            if (SUCCEEDED(hres))
            {
                hres = psfHandler->GetUIObjectOf(hwnd, 1, apidl, riid, prgfInOut, ppvOut);
                psfHandler->Release();
            }
            return hres;
        }
    }
    else if (IsEqualIID(riid, IID_IExtractIconA) 
         || IsEqualIID(riid, IID_IExtractIconW) 
         || IsEqualIID(riid, IID_IContextMenu)
         || IsEqualIID(riid, IID_IQueryInfo)
         || IsEqualIID(riid, IID_IDataObject))
    {
        //  WARNING - we only support this for one at a time.
        if (cidl == 1)
        {
            hres = _GetUIObjectFromShortcut(apidl[0], riid, ppvOut);
        }
    }
    else if (IsEqualIID(riid, IID_IQueryAssociations))
    {
        //  WARNING - we only support this for one at a time.
        if (cidl == 1)
        {
            hres = _AssocCreate(apidl[0], riid, ppvOut);
        }
    }
        


    return hres;
}

HRESULT CInternetFolder::_InitHistoryStg(IUrlHistoryStg **pphist)
{
    HRESULT hr;
    if (!_phist)
    {
        hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
                IID_PPV_ARG(IUrlHistoryStg, &_phist));
    }
    
    if (_phist)
    {
        *pphist = _phist;
        _phist->AddRef();
        return S_OK;
    }

    return hr;
}

HRESULT CInternetFolder::_GetTitle(LPCWSTR pszUrl, STRRET *pstr)
{
    ASSERT(pszUrl);

    IUrlHistoryStg *phist;

    HRESULT hr = _InitHistoryStg(&phist);

    if (SUCCEEDED(hr))
    {
        ASSERT(phist);
        STATURL stat = {0};
        hr = phist->QueryUrl(pszUrl, STATURL_QUERYFLAG_NOURL, &stat);

        if (SUCCEEDED(hr) && stat.pwcsTitle)
        {
            hr = StringToStrRet(stat.pwcsTitle, pstr); 
            CoTaskMemFree(stat.pwcsTitle);
        }
        else
            hr = E_FAIL;

        phist->Release();
    }

    return hr;
}

HRESULT CInternetFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pstr)
{
    IShellFolder *psfHandler;
    HRESULT hr = _CreateProtocolHandlerFromPidl(pidl, NULL, &psfHandler);
    if (hr != S_FALSE)
    {
        if (SUCCEEDED(hr))
        {
            hr = psfHandler->GetDisplayNameOf(pidl, uFlags, pstr);
            psfHandler->Release();
        }
        return hr;
    }

    // FEATURE ZEKEL - should i handle more SHGDN flags here?? - Zekel - 24-NOV-98
    PCURLID purlid = _IsValidUrlID(pidl);
    if (purlid)
    {
        WCHAR sz[MAX_URL_STRING];

        _ExtractURL(purlid, sz, SIZECHARS(sz));

        if (SHGDN_NORMAL != uFlags)
            hr = StringToStrRet(sz, pstr); 
        else
        {
            hr = _GetTitle(sz, pstr);

            //  fallback to the URL if necessary
            if (FAILED(hr))
                hr = StringToStrRet(sz, pstr); 
        }
    }
    else
        hr = E_INVALIDARG;

    return hr;
}

HRESULT CInternetFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
                                 LPCOLESTR pszName, DWORD uFlags,
                                 LPITEMIDLIST * ppidlOut)
{
    IShellFolder *psfHandler;
    HRESULT hres = _CreateProtocolHandlerFromPidl(pidl, NULL, &psfHandler);
    if (hres != S_FALSE)
    {
        if (SUCCEEDED(hres))
        {
            hres = psfHandler->SetNameOf(hwnd, pidl, pszName, uFlags, ppidlOut);
            psfHandler->Release();
        }
        return hres;
    }

    return E_FAIL;
}


HRESULT CInternetFolder::GetClassID(CLSID *pClassID)
{
    *pClassID = CLSID_Internet;
    return S_OK;
}


// IBrowserFrameOptions
HRESULT CInternetFolder::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
{
    // The only case I know of that we hit this code is when you select "Internet Explorer" in the
    // Folder Browser Band.
    HRESULT hr = E_INVALIDARG;

    if (pdwOptions)
    {
        // CInternetFolder should only be used for the "Internet Explorer" pidl that
        // points to the Start Page, so find the start page and substitute it during
        // navigation.
        *pdwOptions |= dwMask & (BFO_SUBSTITUE_INTERNET_START_PAGE | BASE_OPTIONS);
        hr = S_OK;
    }

    return hr;
}



#ifdef DEBUG
extern void remove_from_memlist(void *pv);
#endif

STDAPI CInternetFolder_CreateInstance(IUnknown* pUnkOuter, IUnknown **ppunk, LPCOBJECTINFO poi)
{
    CInternetFolder *psf = new CInternetFolder;
    if (psf)
    {
        //
        // HACK:
        //
        //   SHELL32 caches c_sfInetRoot in a static DATA section
        //  and never release it. It caches an instance of CInternetFolder
        //  and never release it. Therefore, we are removing this object
        //  from the to-be-memleak-detected list to avoid a false alarm
        //  assuming that we don't realy leak this object.
        //   Please don't copy it to another place unless you are really
        //  sure that it's OK not to detect leaks in that scenario.
        //  (SatoNa)
        //
        HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
        psf->Release();
        return hr;
    }
    return E_OUTOFMEMORY;
}
    

STDAPI MonikerFromURL(LPCWSTR wszPath, IMoniker** ppmk)
{
    HRESULT hres = CreateURLMoniker(NULL, wszPath, ppmk);
#ifndef UNIX
    if (FAILED(hres)) 
#else
    // BUG BUG : 
    // IEUNIX  : We use to crash on UNIX if we give a very long invalid url
    // in address bar  or  as home page in inetcpl. We used to crash inside 
    // MkParseDisplayName.
    if (FAILED(hres) && lstrlenW(wszPath) < MAX_PATH) 
#endif
    {
        IBindCtx* pbc;
        hres = CreateBindCtx(0, &pbc);
        if (SUCCEEDED(hres)) 
        {
            // Fall back to a system (file) moniker
            ULONG cchEaten = 0;
            hres = MkParseDisplayName(pbc, wszPath, &cchEaten, ppmk);
            pbc->Release();
        }
    }

    return hres;
}

STDAPI MonikerFromString(LPCTSTR szPath, IMoniker** ppmk)
{
    return MonikerFromURL(szPath, ppmk);
}

#if 0
// alternate way to do the below in a more "pure" way on integrated mode

HRESULT InitPSFInternet()
{
    if (g_psfInternet)
        return S_OK;

    IShellFolder *psfTemp;
    IShellFolder *psfDesktop;
    HRESULT hres = SHGetDesktopFolder(&psfDesktop);
    if (SUCCEEDED(hres))
    {
        ULONG cchEaten;
        LPITEMIDLIST pidl;
        WCHAR wszInternet[] = L"::{871C5380-42A0-1069-A2EA-08002B30309D}"; // CLSID_Internet

        hres = psfDesktop->ParseDisplayName(NULL, NULL, wszInternet, &cchEaten, &pidl, NULL);
        if (SUCCEEDED(hres))
        {
            hres = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfTemp));
            SHFree(pidl);
        }
        psfDesktop->Release();
    }

    if (SHInterlockedCompareExchange((void **)&g_psfInternet, psfTemp, 0))
        psfTemp->Release();     // race to the exchange, free dup copy

    return hres;
}
#endif


HRESULT InitPSFInternet()
{
    if (g_psfInternet)
        return S_OK;

    IShellFolder *psfTemp;
    HRESULT hres = CoCreateInstance(CLSID_CURLFolder, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellFolder, &psfTemp));
    if (SUCCEEDED(hres)) 
    {
        IPersistFolder* ppsf;
        hres = psfTemp->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppsf));
        if (SUCCEEDED(hres)) 
        {
            hres = ppsf->Initialize(c_pidlURLRoot);
            if (SUCCEEDED(hres))
            {
                if (SHInterlockedCompareExchange((void **)&g_psfInternet, psfTemp, 0) == 0)
                    psfTemp->AddRef();  // global now holds ref
            }
            ppsf->Release();
        }
        psfTemp->Release();
    }
    return hres;
}

HRESULT _GetInternetRoot(IShellFolder **ppsfRoot)
{
    HRESULT hr = InitPSFInternet();
    *ppsfRoot = NULL;

    if (SUCCEEDED(hr))
    {
        g_psfInternet->AddRef();
        *ppsfRoot = g_psfInternet;
    }
    return hr;
}

HRESULT _GetRoot(LPCITEMIDLIST pidl, BOOL fIsUrl, IShellFolder **ppsfRoot)
{
    HRESULT hr;
    *ppsfRoot = NULL;
    
    if (fIsUrl)
    {
        ASSERT(IsURLChild(pidl, TRUE));
        TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using the Internet", pidl);
        hr = _GetInternetRoot(ppsfRoot);
    }
    else
    {
        ASSERT(ILIsRooted(pidl));
        TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using Rooted", pidl);

        CLSID clsid;

        ILRootedGetClsid(pidl, &clsid);

        if (IsEqualGUID(clsid, CLSID_ShellDesktop))
        {
            hr = SHBindToObject(NULL, IID_IShellFolder, ILRootedFindIDList(pidl), (void **)ppsfRoot);
        }
        else
        {
            IShellFolder *psf;
            hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IShellFolder, &psf));
            if (SUCCEEDED(hr))
            {
                LPCITEMIDLIST pidlRoot = ILRootedFindIDList(pidl);
                if (!pidlRoot)
                    pidlRoot = &s_idlNULL;

                IPersistFolder* ppf;
                hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf));
                if (SUCCEEDED(hr))
                {
                    hr = ppf->Initialize(pidlRoot);
                    ppf->Release();
                }

                //  hand over the reference
                *ppsfRoot = psf;
            }
        }
    }

    return hr;
}


STDAPI_(BOOL) IEILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fIgnoreHidden)
{
    UINT cb = ILGetSize(pidl1);

    if (cb != ILGetSize(pidl2) || 0 != memcmp(pidl1, pidl2, cb))
    {
        //  THEY are binarily different
        BOOL fRet = FALSE;
        BOOL fWebOnly = FALSE;

        if (IsURLChild(pidl1, TRUE) || IsURLChild(pidl2, TRUE))
            fWebOnly = TRUE;
       
        if ((IsURLChild(pidl1, FALSE) && IsURLChild(pidl2, FALSE))
        || (ILIsRooted(pidl1) && ILIsEqualRoot(pidl1, pidl2)))
        {
            IShellFolder *psf;
            if (SUCCEEDED(_GetRoot(pidl1, fWebOnly, &psf)))
            {
                if (0 == psf->CompareIDs(0, _ILNext(pidl1), _ILNext(pidl2)))
                    fRet = TRUE;

                psf->Release();
            }
        }
        
        if (!fRet && !fWebOnly)
        {
#undef ILIsEqual
            fRet = ILIsEqual(pidl1, pidl2);
        }
        
        if (fRet && !fIgnoreHidden)
        {
            fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_URLFRAGMENT));

            if (fRet)
                fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_URLQUERY));

            if (fRet)
                fRet = (0 == ILCompareHiddenString(pidl1, pidl2, IDLHID_NAVIGATEMARKER));
        }
        return fRet;
    }
    
    return TRUE;
}

// pszName must be MAX_URL_STRING
STDAPI IEGetDisplayName(LPCITEMIDLIST pidl, LPWSTR pszName, UINT uFlags)
{
    return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszName, MAX_URL_STRING, NULL);
}

#ifdef UNIX
//IEUNIX : No support for internal and external names in hp and solaris linker.
extern "C" STDAPI IEGetDisplayNameW(LPCITEMIDLIST pidl, LPWSTR pszName, UINT uFlags)
{
    return IEGetDisplayName(pidl, pszName, uFlags);
}
#endif

HRESULT _GetInternetFolderName(LPWSTR pszName, DWORD cchName)
{
    LPCTSTR pszKey;
    DWORD cbSize = CbFromCch(cchName);

    if (4 > GetUIVersion())
        pszKey = TEXT("CLSID\\{FBF23B42-E3F0-101B-8488-00AA003E56F8}");
    else
        pszKey = TEXT("CLSID\\{871C5380-42A0-1069-A2EA-08002B30309D}"); 

    if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszKey, NULL, NULL, pszName, &cbSize)
    && *pszName)
        return S_OK;

    if (MLLoadString(IDS_REG_THEINTERNET, pszName, cchName)
    && *pszName)
        return S_OK;

    return E_UNEXPECTED;
}

STDAPI IEGetNameAndFlagsEx(LPCITEMIDLIST pidl, UINT uSHFlags, DWORD dwIEFlags, LPWSTR pszName, DWORD cchName, DWORD *prgfInOutAttrs)
{
    HRESULT hres = E_FAIL;

    if (pszName)
    {
        VDATEINPUTBUF(pszName, TCHAR, cchName);
        *pszName = 0;
    }

    //  for support of NON-integrated builds, and 
    //  to expedite handling of URLs while browsing
    if (IsURLChild(pidl, FALSE)) 
    {
        hres = InitPSFInternet();
        if (SUCCEEDED(hres))
        {
            if (pszName)
            {
                STRRET str;
                hres = g_psfInternet->GetDisplayNameOf(_ILNext(pidl), uSHFlags, &str);
                if (SUCCEEDED(hres))
                {
                    StrRetToBufW(&str, pidl, pszName, cchName);
                }
            }

            if (prgfInOutAttrs)
                hres = IEGetAttributesOf(pidl, prgfInOutAttrs);
            
        }
    } 
    else if (GetUIVersion() <= 4 && IsURLChild(pidl, TRUE))
    {
        //
        //  we need to support requests for the Internet SFs
        //  Friendly name.  on NT5 we will always have something
        //  even when the SF is hidden.  but on older versions
        //  of the shell, it was possible to delete the folder
        //  by just removing the icon from the desktop
        //

        if (pszName)
            hres = _GetInternetFolderName(pszName, cchName);

        if (prgfInOutAttrs)
            hres = IEGetAttributesOf(pidl, prgfInOutAttrs);
    }
    else if (ILIsRooted(pidl))
    {
        IShellFolder *psf;
        LPCITEMIDLIST pidlChild;
        
        hres = IEBindToParentFolder(pidl, &psf, &pidlChild);
        if (SUCCEEDED(hres))
        {
            if (pszName)
            {
                STRRET str;
                hres = IShellFolder_GetDisplayNameOf(psf, pidlChild, uSHFlags, &str, 0);
                if (SUCCEEDED(hres))
                {
                    hres = StrRetToBufW(&str, pidlChild, pszName, cchName);
                }
            }

            if (prgfInOutAttrs)
                hres = psf->GetAttributesOf(ILIsEmpty(pidlChild) ? 0 : 1, &pidlChild, prgfInOutAttrs);

            psf->Release();
        }
    } 
    else
        hres = SHGetNameAndFlags(pidl, uSHFlags, pszName, cchName, prgfInOutAttrs);

    if (SUCCEEDED(hres) && pszName && (uSHFlags & SHGDN_FORPARSING))
    {
        hres = _CombineHidden(pidl, dwIEFlags, pszName, cchName);
    }

    TraceMsg(TF_URLNAMESPACE, "IEGDN(%s) returning %x", pszName, hres);
    return hres;
}

STDAPI IEGetNameAndFlags(LPCITEMIDLIST pidl, UINT uFlags, LPWSTR pszName, DWORD cchName, DWORD *prgfInOutAttrs)
{
    return IEGetNameAndFlagsEx(pidl, uFlags, 0, pszName, cchName, prgfInOutAttrs);
}


BOOL _ClassIsBrowsable(LPCTSTR pszClass)
{
    BOOL fRet = FALSE;
    HKEY hk;
    
    if (SUCCEEDED(AssocQueryKey(0, ASSOCKEY_CLASS, pszClass, NULL, &hk)))
    {
        fRet = (NOERROR == RegQueryValueEx(hk, TEXT("DocObject"), NULL, NULL, NULL, NULL)
             || NOERROR == RegQueryValueEx(hk, TEXT("BrowseInPlace"), NULL, NULL, NULL, NULL));

        RegCloseKey(hk);
    }

    return fRet;
}

BOOL _MimeIsBrowsable(LPCTSTR pszExt)
{
    BOOL fRet = FALSE;
    TCHAR sz[MAX_PATH];
    DWORD dwSize = ARRAYSIZE(sz);         

    if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_CONTENTTYPE, pszExt, NULL, sz, &dwSize)))
    {
        TCHAR szKey[MAX_PATH];
        dwSize = SIZEOF(sz);

        // Get the CLSID for the handler of this content type.
        wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("MIME\\Database\\Content Type\\%s"), sz);

        // reuse sz for the clsid
        if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, szKey, TEXT("CLSID"), NULL, (void *) sz, &dwSize))
        {
            fRet = _ClassIsBrowsable(sz);
        }
    }
    return fRet;
}
                    
BOOL _StorageIsBrowsable(LPCTSTR pszPath)
{
    BOOL fRet = FALSE;
    //
    // If the file is STILL not browsable, try to open it as a structured storage
    // and check its CLSID.
    //
    IStorage *pStg = NULL;

    if (StgOpenStorage(pszPath, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &pStg ) == S_OK && pStg)
    {
        STATSTG  statstg;
        if (pStg->Stat( &statstg, STATFLAG_NONAME ) == S_OK)
        {
            TCHAR szClsid[GUIDSTR_MAX];
            SHStringFromGUIDW(statstg.clsid, szClsid, SIZECHARS(szClsid));
            
            fRet = _ClassIsBrowsable(szClsid);
        }
        
        pStg->Release();
    }

    return fRet;
}

BOOL _IEIsBrowsable(LPCITEMIDLIST pidl)
{
    TCHAR szPath[MAX_PATH];
    BOOL fRet = FALSE;
    
    if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath)))
    {
        // Don't change the order of the following OR'd conditions because
        // we want the HTML test to go first.  Also, the NT5 shell will
        // do the ClassIsBrowsable check for us, so we should avoid repeating
        // that check.

        if (PathIsHTMLFile(szPath)
        || _ClassIsBrowsable(szPath)
        || _MimeIsBrowsable(PathFindExtension(szPath))
        || _StorageIsBrowsable(szPath))
            fRet = TRUE;
    }
    
    return fRet;
}


HRESULT _IEGetAttributesOf(LPCITEMIDLIST pidl, DWORD* pdwAttribs, BOOL fAllowExtraChecks)
{
    HRESULT hres = E_FAIL;
    DWORD dwAttribs = *pdwAttribs;
    BOOL fExtraCheckForBrowsable = FALSE;

    //
    // REARCHITECT - Check if we need to execute an additional logic - ZekeL - 7-JAN-99
    // to see if it's browsable or not. this is necessary on shell32s from NT4/win95/IE4 
    // both NT4/win95 have no notion of SFGAO_BROWSABLE, and even though
    // IE4 does, it doesnt handle it correctly for UNICODE file names.
    // We are just as thorough (more) in our private check, so just defer to it.  
    //
    // 78777: Even if we are on NT5, IE can browse things that Shell thinks is not 
    // browsable, for example, .htm files when Netscape is the default browser.  
    // So we should do the extra check on every platform.

    if (fAllowExtraChecks && (dwAttribs & SFGAO_BROWSABLE)) 
    {
        dwAttribs |= SFGAO_FILESYSTEM | SFGAO_FOLDER;
        fExtraCheckForBrowsable = TRUE;
    }

    IShellFolder* psfParent;
    LPCITEMIDLIST pidlChild;
    
    if (ILIsEmpty(pidl))
    {
        hres = SHGetDesktopFolder(&psfParent);
        pidlChild = pidl;
    }
    else if (ILIsRooted(pidl) && ILIsEmpty(_ILNext(pidl)))
    {
        //  
        //  when getting the attributes of the root itself, we
        //  decide its better to just limit the attribs to 
        //  some easily supported subset.  we used to always
        //  fail, but that is a little extreme.
        //
        //  we could also try to get the attributes from HKCR\CLSID\{clsid}\shellfolder\attributes
        //
        *pdwAttribs &= (SFGAO_FOLDER);
        return S_OK;
    }
    else 
    {
        if (GetUIVersion() < 4 && IsURLChild(pidl, TRUE))
        {
            IShellFolder *psfRoot;
            //
            //  if we are Browser Only, and this is the
            //  internet folder itself that we are interested
            //  in, then we need to bind to it by hand
            //  and query it with cidl = 0
            //
            hres = _GetInternetRoot(&psfRoot);

            if (SUCCEEDED(hres))
            {
                hres = SHBindToFolderIDListParent(psfRoot, _ILNext(pidl), IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild);
                psfRoot->Release();
            }
        }
        else if (ILIsRooted(pidl))
            hres = IEBindToParentFolder(pidl, &psfParent, &pidlChild);
        else
            hres = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psfParent), &pidlChild);
    }

    if (SUCCEEDED(hres))
    {
        ASSERT(psfParent);
        hres = psfParent->GetAttributesOf(ILIsEmpty(pidlChild) ? 0 : 1, &pidlChild, &dwAttribs);

        if (FAILED(hres))
            TraceMsg(TF_WARNING, "IEGetAttribs psfParent->GetAttr failed %x", hres);

        psfParent->Release();
    }   
    else
        TraceMsg(TF_WARNING, "IEGetAttribs BindTOParent failed %x", hres);

    //
    //  This is the extra logic we need to execute if this is a browser
    // only mode to get the right "browsable" attribute flag to DocObjects.
    //
    if (fExtraCheckForBrowsable && !(dwAttribs & SFGAO_BROWSABLE))
    {
        if ((dwAttribs & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) 
        {
            if (_IEIsBrowsable(pidl))
                dwAttribs |= SFGAO_BROWSABLE;
        }
    }

    *pdwAttribs &= dwAttribs;
    return hres;
}

HRESULT IEGetAttributesOf(LPCITEMIDLIST pidl, DWORD* pdwAttribs)
{
    return _IEGetAttributesOf(pidl, pdwAttribs, TRUE);
}

// BRYANST: 7/22/97  -  NT Bug #188099
// shell32.dll in IE4 SI and only in that version had a bug if pbc was passed
// to IShellFolder::BindToObject() (fstreex.c!FSBindToFSFolder), it would fail
// to bind to Shell Extensions that extended file system folders, such as:
// the history folder, the occache, etc.  We work around this by passing a NULL pbc
// if the destination is an IE4 shell32.dll and it will go thru FSBindToFSFolder().
BOOL ShouldWorkAroundBCBug(LPCITEMIDLIST pidl)
{
    BOOL fWillBCCauseBug = FALSE;

    if (4 == GetUIVersion())
    {
        LPITEMIDLIST pidlCopy = ILClone(pidl);
        LPITEMIDLIST pidlIterate = pidlCopy;

        // Skip the first two ItemIDs. (#1 could be My Computer)
        if (!ILIsEmpty(pidlIterate))
        {
            IShellFolder * psf;

            // (#2 could be CFSFolder::BindToObject())
            pidlIterate = _ILNext(pidlIterate);
            if (!ILIsEmpty(pidlIterate))
            {
                pidlIterate = _ILNext(pidlIterate);
                // Remove everything else so we bind directly to CFSFolder::BindToObject()
                pidlIterate->mkid.cb = 0;

                if (SUCCEEDED(IEBindToObject(pidlCopy, &psf)))
                {
                    IPersist * pp;

                    if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IPersist, &pp))))
                    {
                        CLSID clsid;

                        if (SUCCEEDED(pp->GetClassID(&clsid)) && 
                            IsEqualCLSID(clsid, CLSID_ShellFSFolder))
                        {
                            fWillBCCauseBug = TRUE;
                        }

                        pp->Release();
                    }
                    psf->Release();
                }
            }

        }

        ILFree(pidlCopy);
    }

    return fWillBCCauseBug;
}

typedef enum
{
    SHOULDBIND_DOCOBJ,
    SHOULDBIND_DESKTOP,
    SHOULDBIND_NONE,
} SHOULDBIND;

//
//  _ShouldDocObjBind() 
//  returns 
//      SHOULDBIND_DOCOBJ   -  Should just use DocObjectFolder directly
//      SHOULDBIND_DESKTOP  -  bind through the desktop
//      SHOULDBIND_NONE     -  FAIL the bind...
//
SHOULDBIND _ShouldDocObjBind(DWORD dwAttribs, BOOL fStrictBind)
{
    if (fStrictBind)
    {
        if ((dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM)) == (SFGAO_BROWSABLE | SFGAO_FILESYSTEM))
            return SHOULDBIND_DOCOBJ;
        else
            return SHOULDBIND_DESKTOP;
    }
    else
    {
        if (dwAttribs & (SFGAO_FOLDER | SFGAO_BROWSABLE))
            return SHOULDBIND_DESKTOP;

        // manually bind using our CDocObjectFolder for
        // files which are not DocObject. Without this code, file:
        // to non-Docobject files (such as multi-media files)
        // won't do anything.
        //
        // is is needed for non integraded browser mode 
        //
        if (dwAttribs & SFGAO_FILESYSTEM) 
            return SHOULDBIND_DOCOBJ;
        else
            return SHOULDBIND_NONE;
    }
}

STDAPI _IEBindToObjectInternal(BOOL fStrictBind, LPCITEMIDLIST pidl, IBindCtx * pbc, REFIID riid, void **ppvOut)
{
    IShellFolder *psfTemp;
    HRESULT hr;

    *ppvOut = NULL;

    // Special case:  If we have the pidl for the "Desktop" then just use the Desktop folder itself
    if (ILIsEmpty(pidl))
    {
        hr = SHGetDesktopFolder(&psfTemp);
        if (SUCCEEDED(hr))
        {
            hr = psfTemp->QueryInterface(riid, ppvOut);
            psfTemp->Release();
        }
    } 
    else 
    {
        BOOL fIsUrlChild = IsURLChild(pidl, TRUE);

        if (fIsUrlChild || ILIsRooted(pidl))
        {
            hr = _GetRoot(pidl, fIsUrlChild, &psfTemp);
            if (SUCCEEDED(hr))
            {
                pidl = _ILNext(pidl);
                
                if (!ILIsEmpty(pidl))
                    hr = psfTemp->BindToObject(pidl, pbc, riid, ppvOut);
                else
                    hr = psfTemp->QueryInterface(riid, ppvOut);

                psfTemp->Release();
            }
        }
        else
        {
            // non integrated browser mode will succeed on 
            // BindToObject(IID_IShellFolder) even for things that should 
            // fail (files). to avoid the down stream problems caused by this we
            // filter out things that are not "browseable" up front, 
            //
            // NOTE: this does not work on simple PIDLs

            DWORD dwAttribs = SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM;

            hr = _IEGetAttributesOf(pidl, &dwAttribs, fStrictBind);
            
            if (SUCCEEDED(hr)) 
            {
                switch (_ShouldDocObjBind(dwAttribs, fStrictBind))
                {
                case SHOULDBIND_DOCOBJ:
                    {
                        //
                        // shortcircuit and bind using our CDocObjectFolder for
                        // files which are BROWSABLE. Without this code, file:
                        // to non-Docobject files (such as multi-media files)
                        // won't do anything.
                        //
                        // is is needed for non integraded browser mode 
                        //
                        CDocObjectFolder *pdof = new CDocObjectFolder();

                        TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using DocObjectFolder", pidl);
                        if (pdof)
                        {
                            hr = pdof->Initialize(pidl);
                            if (SUCCEEDED(hr)) 
                                hr = pdof->QueryInterface(riid, ppvOut);
                            pdof->Release();
                        }
                        else
                            hr = E_OUTOFMEMORY;    
                    }
                    break;

                case SHOULDBIND_DESKTOP:
                    {
                        //
                        // This is the normal case. We just bind down through the desktop...
                        //
                        TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) using Desktop", pidl);

                        hr = SHGetDesktopFolder(&psfTemp);
                        if (SUCCEEDED(hr))
                        {
                            // BRYANST: 7/22/97  -  NT Bug #188099
                            // shell32.dll in IE4 SI and only in that version had a bug if pbc was passed
                            // to IShellFolder::BindToObject() (fstreex.c!FSBindToFSFolder), it would fail
                            // to bind to Shell Extensions that extended file system folders, such as:
                            // the history folder, the occache, etc.  We work around this by passing a NULL pbc
                            // if the destination is an IE4 shell32.dll and it will go thru FSBindToFSFolder().
                            if (pbc && ShouldWorkAroundBCBug(pidl))
                            {
                                pbc = NULL;
                            }

                            hr = psfTemp->BindToObject(pidl, pbc, riid, ppvOut);
                            psfTemp->Release();
                        }
                    } 
                    break;

                default:
                    hr = E_FAIL;
                }
            } 
        }
    }

    if (SUCCEEDED(hr) && !*ppvOut)
    {
        // Some NSEs have bugs where they will fail to fill in the
        // out pointer but return SUCCEEDED(hr).  WS_FTP is one example
        // in NT #413950.
        TraceMsg(TF_URLNAMESPACE, "IEBTO() BUG!!! An NSE succeeded but returned a NULL interface pointer.");
        hr = E_FAIL;
    }
    
    TraceMsg(TF_URLNAMESPACE, "IEBTO(%x) returning %x", pidl, hr);

    return hr;
}

STDAPI IEBindToObjectEx(LPCITEMIDLIST pidl, IBindCtx *pbc, REFIID riid, void **ppvOut)
{
    return _IEBindToObjectInternal(TRUE, pidl, pbc, riid, ppvOut);
}

STDAPI IEBindToObject(LPCITEMIDLIST pidl, IShellFolder **ppsfOut)
{
    return _IEBindToObjectInternal(TRUE, pidl, NULL, IID_PPV_ARG(IShellFolder, ppsfOut));
}

STDAPI IEBindToObjectWithBC(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut)
{
    return _IEBindToObjectInternal(TRUE, pidl, pbc, IID_PPV_ARG(IShellFolder, ppsfOut));
}

//  CLASSIC BIND here
HRESULT IEBindToObjectForNavigate(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut)
{
    return _IEBindToObjectInternal(FALSE, pidl, pbc, IID_PPV_ARG(IShellFolder, ppsfOut));
}


//
// CDwnCodePage: Dummy supports IBindCtx interface object only for casting
//               It holds codepage info to pass via LPBC parameter
//
class CDwnCodePage : public IBindCtx
                   , public IDwnCodePage
{
public:
    // IUnknown methods
    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    // IBindCtx methods
    STDMETHODIMP RegisterObjectBound(IUnknown *punk) { return (_pbc ? _pbc->RegisterObjectBound(punk) : E_NOTIMPL); };
    STDMETHODIMP RevokeObjectBound(IUnknown *punk) { return (_pbc ? _pbc->RevokeObjectBound(punk) : E_NOTIMPL); };
    STDMETHODIMP ReleaseBoundObjects(void) { return (_pbc ? _pbc->ReleaseBoundObjects() : E_NOTIMPL); };
    STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts) { return (_pbc ? _pbc->SetBindOptions(pbindopts) : E_NOTIMPL); };
    STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts) { return (_pbc ? _pbc->GetBindOptions(pbindopts) : E_NOTIMPL); };
    STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot) { *pprot = NULL; return (_pbc ? _pbc->GetRunningObjectTable(pprot) : E_NOTIMPL); };
    STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk) { return (_pbc ? _pbc->RegisterObjectParam(pszKey, punk) : E_NOTIMPL); };
    STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk) { *ppunk = NULL; return (_pbc ? _pbc->GetObjectParam(pszKey, ppunk) : E_NOTIMPL); };
    STDMETHODIMP EnumObjectParam(IEnumString **ppenum) { *ppenum = NULL; return (_pbc ? _pbc->EnumObjectParam(ppenum) : E_NOTIMPL); };
    STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey) { return (_pbc ? _pbc->RevokeObjectParam(pszKey) : E_NOTIMPL); };

    STDMETHODIMP RemoteSetBindOptions(BIND_OPTS2 *pbindopts) { return E_NOTIMPL; };
    STDMETHODIMP RemoteGetBindOptions(BIND_OPTS2 *pbindopts) { return E_NOTIMPL; };

    // IDwnCodePage methods
    STDMETHODIMP_(UINT) GetCodePage(void) { return _uiCodePage; };
    STDMETHODIMP SetCodePage(UINT uiCodePage) { _uiCodePage = uiCodePage; return S_OK; };

    // Constructor
    CDwnCodePage(IBindCtx * pbc, UINT uiCodePage) : _cRef(1) { _uiCodePage = uiCodePage; _pbc = NULL; IUnknown_Set((IUnknown **)&_pbc, (IUnknown *)pbc); };
    ~CDwnCodePage() { ATOMICRELEASE(_pbc); };

private:
    int     _cRef;
    UINT    _uiCodePage;
    IBindCtx * _pbc;
};

STDAPI CDwnCodePage::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENT(CDwnCodePage, IBindCtx),
        QITABENT(CDwnCodePage, IDwnCodePage), 
        { 0 },
    };
    return QISearch(this, qit, riid, ppvObj);
}

STDAPI_(ULONG) CDwnCodePage::AddRef()
{
    _cRef++;
    return _cRef;
}

STDAPI_(ULONG) CDwnCodePage::Release()
{
    _cRef--;
    if (0 < _cRef)
        return _cRef;

    delete this;
    return 0;
}

// IEParseDisplayName() will do all of the below functionality in IECreateFromPathCPWithBC()
// plus the following two things:
// 1.  It will call ParseURLFromOutsideSource(), so this is more friendly to
//     strings from outside sources.
// 2.  If the URL has a fragment, this function will pass out a PIDL with the last
//     ID being the location.
HRESULT IECreateFromPathCPWithBCW(UINT uiCP, LPCWSTR pszPath, IBindCtx * pbc, LPITEMIDLIST *ppidlOut)
{
    TraceMsg(TF_URLNAMESPACE, "IECFP(%s) called", pszPath);

    HRESULT hr = S_OK;
    WCHAR szPath[MAX_URL_STRING];
    WCHAR szBuf[MAX_PATH];
    DWORD cchBuf = ARRAYSIZE(szBuf);
    CDwnCodePage DwnCodePage(pbc, uiCP);
    DWORD len;

    // Initialize for failure case
    *ppidlOut = NULL;

    // if we are passed a NULL path, then there is no way we can convert it to a pidl.
    // in some cases the reason we are passed a NULL path is because the IShellFolder
    // provider was unable to generate a parseable display name (MSN Classic 1.3 is
    // a very good example, they return E_NOTIMPL).
    if ( ((len = lstrlen( pszPath )) == 0)  || len >= MAX_URL_STRING )
    {
        return E_FAIL;
    }

    // Is this a "file:" URL?
    if (IsFileUrlW(pszPath) && SUCCEEDED(hr = PathCreateFromUrl(pszPath, szBuf, &cchBuf, 0)))
        pszPath = szBuf;

    BOOL fIsFilePath = PathIsFilePath(pszPath);

#ifdef FEATURE_IE_USE_DESKTOP_PARSING
    //
    //  in order to take advantage of whatever enhancements the desktop
    //  makes to parsing (eg, WebFolders and shell: URLs), then we allow
    //  the desktop first go at it.  it will loop back into the internet
    //  shell folder if all the special cases fail.
    //      maybe use a reg setting to control???
    //
    //
    if (fIsFilePath || GetUIVersion() >= 5)
#else // !FEATURE_IE_USE_DESKTOP_PARSING
    //
    //  right now we just use the desktop if its a file path or
    //  it is a shell: URL on NT5
    //
    if (fIsFilePath || (GetUIVersion() >= 5 && URL_SCHEME_SHELL == GetUrlSchemeW(pszPath)))
#endif // FEATURE_IE_USE_DESKTOP_PARSING
    {
        ASSERT(SUCCEEDED(hr));
        
        // Resolve any dot-dot path reference and remove trailing backslash
        if (fIsFilePath)
        {
            PathCanonicalize(szPath, pszPath);
            pszPath = szPath;

            // This call will cause a network hit: one connection attempt to \\server\IPC$
            // and then a series of FindFirst's - one for each directory.
            if (StrChr(pszPath, L'*') || StrChr(pszPath, L'?'))
            {
                hr = E_FAIL;
            }
        }
        
        if (SUCCEEDED(hr))
        {
            hr = SHILCreateFromPath(pszPath, ppidlOut, NULL);               
            TraceMsg(DM_CDOFPDN, "IECreateFromPath psDesktop->PDN(%s) returned %x", pszPath, hr);
        }
    }
    else
    {
        //
        // Need to put in buffer since ParseDisplayName doesn't take a 'const' string.
        StrCpyN(szPath, pszPath, ARRAYSIZE(szPath));
        pszPath = szPath;

        // Avoid the network and disk hits above for non-file urls.
        // This code path is taken on http: folks so a nice optimization. We will then drop
        // down below where we check the internet namespace.
        IShellFolder *psfRoot;
        hr = _GetInternetRoot(&psfRoot);
        if (SUCCEEDED(hr))
        {
            TraceMsg(TF_URLNAMESPACE, "IECFP(%s) calling g_psfInternet->PDN %x", pszPath, hr);
            LPITEMIDLIST pidlRel;

            hr = psfRoot->ParseDisplayName(NULL, (IBindCtx*)&DwnCodePage, szPath, NULL, &pidlRel, NULL);
            TraceMsg(DM_CDOFPDN, "IECreateFromPath called psfInternet->PDN(%s) %x", pszPath, hr);
            if (SUCCEEDED(hr))
            {
                *ppidlOut = ILCombine(c_pidlURLRoot, pidlRel);
                if (!*ppidlOut)
                    hr = E_OUTOFMEMORY;
                ILFree(pidlRel);
            }
            
            psfRoot->Release();
        }

    }

    // NOTE: NT5 beta 3 and before had a call to SHSimpleIDListFromPath().
    //       This is very bad because it will parse any garbage and prevent
    //       the caller from finding invalid strings.  I(BryanSt) needed
    //       this fixed for IEParseDisplayNameWithBCW() would fail on invalid
    //       address bar strings ("Search Get Rich Quick").
    TraceMsg(TF_URLNAMESPACE, "IECFP(%s) returning %x (hr=%x)",
             pszPath, *ppidlOut, hr);

    return hr;
}

HRESULT IECreateFromPathCPWithBCA(UINT uiCP, LPCSTR pszPath, IBindCtx * pbc, LPITEMIDLIST *ppidlOut)
{
    WCHAR szPath[MAX_URL_STRING];

    ASSERT(lstrlenA(pszPath) < ARRAYSIZE(szPath));
    SHAnsiToUnicodeCP(uiCP, pszPath, szPath, ARRAYSIZE(szPath));

    return IECreateFromPathCPWithBCW(uiCP, szPath, pbc, ppidlOut);
}


HRESULT IEParseDisplayName(UINT uiCP, LPCTSTR pszPath, LPITEMIDLIST * ppidlOut)
{
    return IEParseDisplayNameWithBCW(uiCP, pszPath, NULL, ppidlOut);
}


// This function will do two things that IECreateFromPathCPWithBC() will not do:
// 1.  It will add the "Query" section of the URL into the pidl.
// 2.  If the URL has a fragment, this function will pass out a PIDL with the last
//     ID being the location.
// NOTE: If the caller needs the string to be "cleaned up" because the user manually
//       entered the URL, the caller needs to call ParseURLFromOutsideSource() before
//       calling this function.  That function should only be called on strings entered
//       by the user because of the perf hit and it could incorrectly format valid
//       parsible display names.  For example, ParseURLFromOutsideSource() will
//       convert the string "My Computer" into a search URL for yahoo.com
//       (http://www.yahoo.com/search.asp?p=My+p=Computer) when some callers
//       want that string parsed by an IShellFolder in the desktop.
HRESULT IEParseDisplayNameWithBCW(UINT uiCP, LPCWSTR pwszPath, IBindCtx * pbc, LPITEMIDLIST * ppidlOut)
{
    TCHAR szPath[MAX_URL_STRING];
    LPCWSTR pwszFileLocation = NULL;
    WCHAR szQuery[MAX_URL_STRING];
    HRESULT hres;

    szQuery[0] = TEXT('\0');
#ifdef DEBUG
    if (IsFlagSet(g_dwDumpFlags, DF_URL)) 
    {
        TraceMsg(DM_TRACE, "IEParseDisplayName got %s", szPath);
    }
#endif

    //  We want to remove QUERY and FRAGMENT sections of
    //  FILE URLs because they need to be added in "Hidden" pidls.
    //  Also, URLs need to be escaped all the time except for paths
    //  to facility parsing and because we already removed all other
    //  parts of the URL (Query and Fragment).
    if (IsFileUrlW(pwszPath)) 
    {
        DWORD cchQuery = SIZECHARS(szQuery) - 1;
        
        pwszFileLocation = UrlGetLocationW(pwszPath);        

        if (SUCCEEDED(UrlGetPart(pwszPath, szQuery+1, &cchQuery, URL_PART_QUERY, 0)) && cchQuery)
            szQuery[0] = TEXT('?');

        DWORD cchPath = ARRAYSIZE(szPath);
        if (FAILED(PathCreateFromUrl(pwszPath, szPath, &cchPath, 0))) 
        {
            // Failed to parse it back. Use the original.
            StrCpyN(szPath, pwszPath, ARRAYSIZE(szPath));
        }
    }        
    else 
    {
        // If we failed, just try to use the original
        StrCpyN(szPath, pwszPath, ARRAYSIZE(szPath));
    }

#ifdef DEBUG
    if (IsFlagSet(g_dwDumpFlags, DF_URL)) 
        TraceMsg(DM_TRACE, "IEParseDisplayName calling IECreateFromPath %s", szPath);
#endif

    hres = IECreateFromPathCPWithBC(uiCP, szPath, pbc, ppidlOut);
    if (SUCCEEDED(hres) && pwszFileLocation)
    {
        ASSERT(*ppidlOut);
        *ppidlOut = IEILAppendFragment(*ppidlOut, pwszFileLocation);
        hres = *ppidlOut ? S_OK : E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hres) && szQuery[0] == TEXT('?'))
    {
        *ppidlOut = ILAppendHiddenString(*ppidlOut, IDLHID_URLQUERY, szQuery);
        hres = *ppidlOut ? S_OK : E_OUTOFMEMORY;
    }

    return hres;
}
