#include "shellprv.h"
#include "util.h"
#include "ids.h"
#include "infotip.h"
#include "fstreex.h"
#include "lm.h"
#include "shgina.h"
#include "prop.h"
#include "datautil.h"
#include "filefldr.h"
#include "buytasks.h"
#pragma hdrstop


// this define causes the shared folder code to work on domains (for debug)
//#define SHOW_SHARED_FOLDERS 

// filter out the current user accounts
#define FILTER_CURRENT_USER 0

// where do we store the doc folder paths
#define REGSTR_PATH_DOCFOLDERPATH  TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DocFolderPaths")

// state API for showing the shared documents folder

STDAPI_(BOOL) SHShowSharedFolders()
{
#ifndef SHOW_SHARED_FOLDERS
    // restriction overrides all logic for the shared documents

    if (SHRestricted(REST_NOSHAREDDOCUMENTS))
        return FALSE;

    // if we haven't computed the "show shared folders flag" then do so

    static int iShow = -1;
    if (iShow == -1)      
        iShow = IsOS(OS_DOMAINMEMBER) ? 0:1;    // only works if we are not a domain user

    return (iShow >= 1);
#else
    return true;
#endif
}


// implementation of a delegate shell folder for merging in shared documents

STDAPI_(void) SHChangeNotifyRegisterAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias);

HRESULT CSharedDocsEnum_CreateInstance(HDPA hItems, DWORD grfFlags, IEnumIDList **ppenum);

#pragma pack(1)
typedef struct
{
    // these memebers overlap DELEGATEITEMID struct
    // for our IDelegateFolder support
    WORD cbSize;
    WORD wOuter;
    WORD cbInner;

    // our stuff
    DWORD dwType;               // our type of folder
    TCHAR wszID[1];             // unique ID for the user
} SHAREDITEM;
#pragma pack()

typedef UNALIGNED SHAREDITEM * LPSHAREDITEM;
typedef const UNALIGNED SHAREDITEM * LPCSHAREDITEM;

#define SHAREDID_COMMON 0x0
#define SHAREDID_USER   0x2


class CSharedDocuments : public IDelegateFolder, IPersistFolder2, IShellFolder2, IShellIconOverlay
{
public:
    CSharedDocuments();
    ~CSharedDocuments();

    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    // IDelegateFolder
    STDMETHODIMP SetItemAlloc(IMalloc *pmalloc);

    // IPersist
    STDMETHODIMP GetClassID(CLSID* pclsid)
        { *pclsid = CLSID_SharedDocuments; return S_OK; }
        
    // IPersistFolder
    STDMETHODIMP Initialize(LPCITEMIDLIST pidl);

    // IPersistFolder2
    STDMETHODIMP GetCurFolder(LPITEMIDLIST* ppidl);

    // IShellFolder
    STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes);
    STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
    STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
    STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
        { return BindToObject(pidl, pbc, riid, ppv); }
    STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
    STDMETHODIMP CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
        { return E_NOTIMPL; }
    STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut);
    STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl,REFIID riid, UINT* prgfInOut, void **ppv);
    STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
    STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD uFlags, LPITEMIDLIST* ppidlOut)
        { return E_NOTIMPL; }

    // IShellFolder2
    STDMETHODIMP GetDefaultSearchGUID(LPGUID lpGuid)
        { return E_NOTIMPL; }
    STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum)
        { return E_NOTIMPL; }
    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);
    STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
        { return E_NOTIMPL; }
    STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid)
        { return E_NOTIMPL; }

    // IShellIconOverlay
    STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
        { return _GetOverlayIndex(pidl, pIndex, FALSE); }
    STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex)
        { return _GetOverlayIndex(pidl, pIconIndex, TRUE); }

private:
    LONG _cRef;
    IMalloc *_pmalloc;
    LPITEMIDLIST _pidl;

    CRITICAL_SECTION _cs;                   // critical section for managing lifetime of the cache

    TCHAR _szCurrentUser[UNLEN+1];          // user name (cached for current user)

    BOOL _fCachedAllUser:1;                 // cached the all user account
    TCHAR _szCachedUser[UNLEN+1];           //  if (FALSE) then this contains the user ID.

    IUnknown *_punkCached;                  // IUnknown object (from FS folder) that we cache
    LPITEMIDLIST _pidlCached;               // IDLIST of the cached folder

    void _ClearCachedObjects();
    BOOL _IsCached(LPCITEMIDLIST pidl);
    HRESULT _CreateFolder(LPBC pbc, LPCITEMIDLIST pidl, REFIID riid, void **ppv, BOOL fRegisterAlias);
    HRESULT _GetTarget(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl);
    HRESULT _GetTargetIDList(BOOL fForceReCache, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl);
    HRESULT _AddIDList(HDPA hdpa, DWORD dwType, LPCTSTR pszUser);
    HRESULT _AllocIDList(DWORD dwType, LPCTSTR pszUser, LPITEMIDLIST *ppidl);
    HRESULT _GetSharedFolders(HDPA *phItems);
    HRESULT _GetAttributesOf(LPCITEMIDLIST pidl, DWORD rgfIn, DWORD *prgfOut);
    LPCTSTR _GetUserFromIDList(LPCITEMIDLIST pidl, LPTSTR pszBuffer, INT cchBuffer);
    HRESULT _GetPathForUser(LPCTSTR pcszUser, LPTSTR pszBuffer, int cchBuffer);
    HRESULT _GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex, BOOL fIcon);

    static HRESULT s_FolderMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdo, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    friend class CSharedDocsEnum;
};


// constructors

CSharedDocuments::CSharedDocuments() :
    _cRef(1)
{
    InitializeCriticalSection(&_cs);
}

CSharedDocuments::~CSharedDocuments()
{
    ATOMICRELEASE(_pmalloc);
    ATOMICRELEASE(_punkCached);

    ILFree(_pidlCached);
    ILFree(_pidl);
    
    DeleteCriticalSection(&_cs);
}

STDAPI CSharedDocFolder_CreateInstance(IUnknown *punkOut, REFIID riid, void **ppv)
{
    CSharedDocuments *psdf = new CSharedDocuments;
    if (!psdf)
        return E_OUTOFMEMORY;

    HRESULT hr = psdf->QueryInterface(riid, ppv);
    psdf->Release();
    return hr;
}


// IUnknown handling

STDMETHODIMP CSharedDocuments::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CSharedDocuments, IDelegateFolder),                                // IID_IDelegateFolder
        QITABENTMULTI(CSharedDocuments, IShellFolder, IShellFolder2),               // IID_IShellFOlder
        QITABENT(CSharedDocuments, IShellFolder2),                                  // IID_IShellFolder2
        QITABENTMULTI(CSharedDocuments, IPersistFolder, IPersistFolder2),           // IID_IPersistFolder
        QITABENTMULTI(CSharedDocuments, IPersist, IPersistFolder2),                 // IID_IPersist
        QITABENT(CSharedDocuments, IPersistFolder2),                                // IID_IPersistFolder2
        QITABENT(CSharedDocuments, IShellIconOverlay),                              // IID_IShellIconOverlay
        QITABENTMULTI2(CSharedDocuments, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject
        { 0 },
    };

    if (riid == CLSID_SharedDocuments)
    {
        *ppv = this;                        // no ref
        return S_OK;
    }

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

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

STDMETHODIMP_(ULONG) CSharedDocuments::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;
            
    delete this;
    return 0;
}

// IDelegateFolder
HRESULT CSharedDocuments::SetItemAlloc(IMalloc *pmalloc)
{
    IUnknown_Set((IUnknown**)&_pmalloc, pmalloc);
    return S_OK;
}


HRESULT CSharedDocuments::Initialize(LPCITEMIDLIST pidl)
{
    ILFree(_pidl);
    return SHILClone(pidl, &_pidl);
}

HRESULT CSharedDocuments::GetCurFolder(LPITEMIDLIST* ppidl)
{
    return SHILClone(_pidl, ppidl);
}


// single level cache for the objects

void CSharedDocuments::_ClearCachedObjects()
{
    ATOMICRELEASE(_punkCached);      // clear out the cached items (old)
    ILFree(_pidlCached);
    _pidlCached = NULL;
}

BOOL CSharedDocuments::_IsCached(LPCITEMIDLIST pidl)
{
    BOOL fResult = FALSE;

    TCHAR szUser[UNLEN+1];
    if (_GetUserFromIDList(pidl, szUser, ARRAYSIZE(szUser)))    
    {
        // did we cache the users account information?

        if (!_szCachedUser[0] || (StrCmpI(_szCachedUser, szUser) != 0))
        {
            _fCachedAllUser = FALSE;
            StrCpyN(_szCachedUser, szUser, ARRAYSIZE(_szCachedUser));
            _ClearCachedObjects();
        }
        else
        {
            fResult = TRUE;             // were set!
        }
    }
    else
    {
        // the all user case is keyed on a flag rather than the
        // account name we are supposed to be using.

        if (!_fCachedAllUser)
        {
            _fCachedAllUser = TRUE;
            _szCachedUser[0] = TEXT('\0');
            _ClearCachedObjects();
        }
        else
        {   
            fResult = TRUE;             // were set
        }
    }

    return fResult;
}

// IShellFolder methods

HRESULT CSharedDocuments::_CreateFolder(LPBC pbc, LPCITEMIDLIST pidl, REFIID riid, void **ppv, BOOL fRegisterAlias)
{
    HRESULT hr = S_OK;

    EnterCriticalSection(&_cs);

    // get the target folder (were already in a critical section) 
    // and then bind down to the shell folder if we have not already
    // cached one for ourselves.

    if (!_IsCached(pidl) || !_punkCached)
    {
        LPITEMIDLIST pidlTarget;
        hr = _GetTargetIDList(TRUE, pidl, &pidlTarget); // clears _punkCached in here (so no leak)
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST pidlInit;
            hr = SHILCombine(_pidl, pidl, &pidlInit);
            if (SUCCEEDED(hr))
            {
                hr = SHCoCreateInstance(NULL, &CLSID_ShellFSFolder, NULL, IID_PPV_ARG(IUnknown, &_punkCached));
                if (SUCCEEDED(hr))
                {
                    IPersistFolder3 *ppf;
                    hr = _punkCached->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf));
                    if (SUCCEEDED(hr))
                    {
                        PERSIST_FOLDER_TARGET_INFO pfti = {0};
                        pfti.pidlTargetFolder = (LPITEMIDLIST)pidlTarget;
                        pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY;
                        pfti.csidl = -1;
                        hr = ppf->InitializeEx(NULL, pidlInit, &pfti);
                        ppf->Release();
                    }

                    if (SUCCEEDED(hr) && fRegisterAlias)
                        SHChangeNotifyRegisterAlias(pidlTarget, pidlInit);

                    if (FAILED(hr))
                    {
                        _punkCached->Release();
                        _punkCached = NULL;
                    }
                }
                ILFree(pidlInit);
            }
            ILFree(pidlTarget);
        }
    }

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

    LeaveCriticalSection(&_cs);
    return hr;
}


HRESULT CSharedDocuments::_GetTarget(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
{
    EnterCriticalSection(&_cs);
    HRESULT hr = _GetTargetIDList(FALSE, pidl, ppidl);
    LeaveCriticalSection(&_cs);
    return hr;
}

HRESULT CSharedDocuments::_GetTargetIDList(BOOL fForceReCache, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
{
    HRESULT hr = S_OK;
    if (fForceReCache || !_IsCached(pidl) || !_pidlCached)
    {
        _ClearCachedObjects();              // we don't have it cached now

        LPCSHAREDITEM psid = (LPCSHAREDITEM)pidl;
        if (psid->dwType == SHAREDID_COMMON)
        {
            hr = SHGetSpecialFolderLocation(NULL, CSIDL_COMMON_DOCUMENTS|CSIDL_FLAG_NO_ALIAS, &_pidlCached);
        }
        else if (psid->dwType == SHAREDID_USER)
        {
            TCHAR szPath[MAX_PATH], szUser[UNLEN+1];
            hr = _GetPathForUser(_GetUserFromIDList(pidl, szUser, ARRAYSIZE(szUser)), szPath, ARRAYSIZE(szPath));
            if (SUCCEEDED(hr))
            {
                hr = ILCreateFromPathEx(szPath, NULL, ILCFP_FLAG_NO_MAP_ALIAS, &_pidlCached, NULL);
            }
        }
        else
        {
            hr = E_INVALIDARG;              // invalid IDLIST passed
        }
    }

    if (SUCCEEDED(hr))
        hr = SHILClone(_pidlCached, ppidl);
    
    return hr;
}

HRESULT CSharedDocuments::_AddIDList(HDPA hdpa, DWORD dwType, LPCTSTR pszUser)
{
    LPITEMIDLIST pidl;
    HRESULT hr = _AllocIDList(dwType, pszUser, &pidl);
    if (SUCCEEDED(hr))
    {
        DWORD grfFlags = SFGAO_FOLDER;
        hr = _GetAttributesOf(pidl, SFGAO_FOLDER, &grfFlags);
        if (SUCCEEDED(hr) && grfFlags & SFGAO_FOLDER)
        {        
            if (-1 == DPA_AppendPtr(hdpa, pidl))
            {
                ILFree(pidl);
                hr = E_OUTOFMEMORY;
            }
            else
            {
                hr = S_OK;
            }
        }
        else
        {
            ILFree(pidl); 
        }   
    }
    return hr;
}


HRESULT CSharedDocuments::_AllocIDList(DWORD dwType, LPCTSTR pszUser, LPITEMIDLIST *ppidl)
{
    DWORD cb = sizeof(SHAREDITEM);
    int cchUser = pszUser ? lstrlen(pszUser) + 1 : 0;

    // ID list contains strings if its a user 
    
    if (dwType == SHAREDID_USER)
        cb += sizeof(TCHAR) * cchUser;

    SHAREDITEM *psid = (SHAREDITEM*)_pmalloc->Alloc(cb);
    if (!psid)
        return E_OUTOFMEMORY;

    psid->dwType = dwType;                  // type is universal

    if (dwType == SHAREDID_USER)
        StrCpyW(psid->wszID, pszUser);   

    *ppidl = (LPITEMIDLIST)psid;
    return S_OK;
}

LPCTSTR CSharedDocuments::_GetUserFromIDList(LPCITEMIDLIST pidl, LPTSTR pszUser, int cchUser)
{
    LPCSHAREDITEM psid = (LPCSHAREDITEM)pidl;

    if (psid->dwType == SHAREDID_COMMON)
    {
        pszUser[0] = 0;               // initialize
        return NULL;
    }

    ualstrcpynW(pszUser, psid->wszID, cchUser);
    return pszUser;
}

HRESULT CSharedDocuments::_GetPathForUser(LPCTSTR pszUser, LPTSTR pszBuffer, int cchBuffer)
{
    HRESULT hr = E_FAIL;
    BOOL fResult = FALSE;

    if (!pszUser)
    {
        // get the common documents path (which covers all users), this user is always defined
        // so lets return TRUE if they just want to check to see if its defined, otherwise
        // just pass out the result from fetching the path.

        fResult = !pszBuffer || (SHGetSpecialFolderPath(NULL, pszBuffer, CSIDL_COMMON_DOCUMENTS, FALSE));
    }
    else
    {
        // we have a user ID, so lets attempt to get the path fro that from the registry
        // if we get it then pass it back to the caller.

        DWORD dwType;
        DWORD cbBuffer = cchBuffer*sizeof(TCHAR);
        if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DOCFOLDERPATH, pszUser, &dwType,  pszBuffer, &cbBuffer))
        {
            fResult = ((dwType == REG_SZ) && cbBuffer);      // did we get a value back?
        }
    }

    if (fResult)
    {
        hr = S_OK;
    }

    return hr;
}

HRESULT CSharedDocuments::_GetSharedFolders(HDPA *phItems)
{
    HRESULT hr = E_OUTOFMEMORY;
    HDPA hItems = DPA_Create(16);
    if (hItems)
    {
        if (!IsUserAGuest()) // all other users' my documents folders should appear in my computer for non-guest users on workgroup machines
        {
            ILogonEnumUsers *peu;
            hr = SHCoCreateInstance(NULL, &CLSID_ShellLogonEnumUsers, NULL, IID_PPV_ARG(ILogonEnumUsers, &peu));
            if (SUCCEEDED(hr))
            {
                UINT cUsers, iUser;
                hr = peu->get_length(&cUsers);
                for (iUser = 0; (cUsers != iUser) && SUCCEEDED(hr); iUser++)
                {
                    VARIANT varUser = {VT_I4};
                    InitVariantFromInt(&varUser, iUser);

                    ILogonUser *plu;
                    hr = peu->item(varUser, &plu);
                    if (SUCCEEDED(hr))
                    {
                        // only show document folders for users that can log in
                        VARIANT_BOOL vbLogonAllowed;
                        hr = plu->get_interactiveLogonAllowed(&vbLogonAllowed);
                        if (SUCCEEDED(hr) && (vbLogonAllowed != VARIANT_FALSE))
                        {
                            // get the user name as this is our key to to the users documents path
                            VARIANT var = {0};
                            hr = plu->get_setting(L"LoginName", &var);
                            if (SUCCEEDED(hr))
                            {
#if FILTER_CURRENT_USER                            
                                if (!_szCurrentUser[0])
                                {
                                    DWORD cchUser = ARRAYSIZE(_szCurrentUser);
                                    if (!GetUserName(_szCurrentUser, &cchUser))
                                    {
                                        _szCurrentUser[0] = TEXT('\0');
                                    }
                                }

                                if (!_szCurrentUser[0] || (StrCmpI(var.bstrVal, _szCurrentUser) != 0))
                                {
                                    HRESULT hrT = _AddIDList(hItems, SHAREDID_USER, var.bstrVal);
                                    if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hrT)
                                    {
                                        SHDeleteValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DOCFOLDERPATH, var.bstrVal);
                                    }                                    
                                }
#else
                                HRESULT hrT = _AddIDList(hItems, SHAREDID_USER, var.bstrVal);
                                if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hrT)
                                {
                                    SHDeleteValue(HKEY_LOCAL_MACHINE, REGSTR_PATH_DOCFOLDERPATH, var.bstrVal);
                                }                                    
#endif
                                VariantClear(&var);
                            }
                        }
                        plu->Release();
                    }
                }

                peu->Release();
            }            
        }

        _AddIDList(hItems, SHAREDID_COMMON, NULL);  
        hr = S_OK;
    }

    *phItems = hItems;
    return hr;
}


// parsing support allows us to pick off SharedDocuments from the root
// of the shell namespace and navigate there - this a canonical name
// that we use for binding to the shared documents folder attached
// to the My Computer namespace.

HRESULT CSharedDocuments::ParseDisplayName(HWND hwnd, LPBC pbc, LPTSTR pszName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes)
{
    HRESULT hr = E_INVALIDARG;
    if (SHShowSharedFolders())
    {
        if (0 == StrCmpI(pszName, L"SharedDocuments"))
        {
            hr = _AllocIDList(SHAREDID_COMMON, NULL, ppidl);
            if (SUCCEEDED(hr) && pdwAttributes)
            {
                hr = _GetAttributesOf(*ppidl, *pdwAttributes, pdwAttributes);
            }
        }
    }
    return hr;
}


// enumerate the shared documents folders

HRESULT CSharedDocuments::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
    *ppenumIDList = NULL;               // no enumerator yet

    HRESULT hr = S_FALSE;
    if (SHShowSharedFolders())
    {
        HDPA hItems;
        hr = _GetSharedFolders(&hItems);
        if (SUCCEEDED(hr))
        {
            hr = CSharedDocsEnum_CreateInstance(hItems, grfFlags, ppenumIDList);
            if (FAILED(hr))
            {
                DPA_FreeIDArray(hItems);
            }
        }
    }
    return hr;
}


// return the display name for the folders that we have

HRESULT CSharedDocuments::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName)
{
    HRESULT hr = S_OK; 
    TCHAR szName[MAX_PATH] = {0};
 
    LPCSHAREDITEM psid = (LPCSHAREDITEM)pidl;
    if (((uFlags & (SHGDN_INFOLDER|SHGDN_FORPARSING)) == SHGDN_INFOLDER) && 
         (psid && (psid->dwType == SHAREDID_USER)))
    {
        // compute the <user>'s Documents name that we will show, we key this on 
        // the user name we have in the IDList and its display string.

        USER_INFO_10 *pui;
        TCHAR szUser[MAX_PATH];
        if (NERR_Success == NetUserGetInfo(NULL, _GetUserFromIDList(pidl, szUser, ARRAYSIZE(szUser)), 10, (LPBYTE*)&pui))
        {
            if (*pui->usri10_full_name)
            {
                StrCpyN(szUser, pui->usri10_full_name, ARRAYSIZE(szUser));
            }
            NetApiBufferFree(pui);
        }     

        TCHAR szFmt[MAX_PATH];
        LoadString(g_hinst, IDS_LOCALGDN_FLD_THEIRDOCUMENTS, szFmt, ARRAYSIZE(szFmt));
        wnsprintf(szName, ARRAYSIZE(szName), szFmt, szUser);
    }
    else
    {
        // all other scenarios dump down to the real folder to get their display
        // name for this folder.

        LPITEMIDLIST pidlTarget;
        hr = _GetTarget(pidl, &pidlTarget);
        if (SUCCEEDED(hr))
        {
            hr = SHGetNameAndFlags(pidlTarget, uFlags, szName, ARRAYSIZE(szName), NULL);
            ILFree(pidlTarget);
        }
    }

    if (SUCCEEDED(hr))
        hr = StringToStrRet(szName, lpName);

    return hr;
}

LONG CSharedDocuments::_GetAttributesOf(LPCITEMIDLIST pidl, DWORD rgfIn, DWORD *prgfOut)
{
    DWORD dwResult = rgfIn;
    LPITEMIDLIST pidlTarget;
    HRESULT hr = _GetTarget(pidl, &pidlTarget);
    if (SUCCEEDED(hr))
    {
        IShellFolder *psf;
        LPCITEMIDLIST pidlChild;
        hr = SHBindToIDListParent(pidlTarget, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
        if (SUCCEEDED(hr))
        {
            hr = psf->GetAttributesOf(1, &pidlChild, &dwResult);
            psf->Release();
        }
        ILFree(pidlTarget);
    }

    if (!SHShowSharedFolders())
        dwResult |= SFGAO_NONENUMERATED;

    *prgfOut = *prgfOut & (dwResult & ~(SFGAO_CANDELETE|SFGAO_CANRENAME|SFGAO_CANMOVE|SFGAO_CANCOPY));

    return hr;
}

HRESULT CSharedDocuments::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut)
{
    ULONG rgfOut = *rgfInOut;

    if (!cidl || !apidl)
        return E_INVALIDARG;

    for (UINT i = 0; i < cidl; i++)
        _GetAttributesOf(apidl[i], *rgfInOut, &rgfOut);

    *rgfInOut = rgfOut;
    return S_OK;
}


// bind through our folder 

HRESULT CSharedDocuments::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{
    HRESULT hr = E_OUTOFMEMORY;
    if (IsEqualIID(riid, IID_IShellIconOverlay))
    {
        hr = this->QueryInterface(riid, ppv);
    }
    else
    {
        LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
        if (pidlFirst)
        {
            IShellFolder *psf;
            hr = _CreateFolder(pbc, pidlFirst, IID_PPV_ARG(IShellFolder, &psf), TRUE);
            if (SUCCEEDED(hr))
            {
                LPCITEMIDLIST pidlNext = _ILNext(pidl);
                if (ILIsEmpty(pidlNext))
                {
                    hr = psf->QueryInterface(riid, ppv);
                }
                else
                {
                    hr = psf->BindToObject(pidlNext, pbc, riid, ppv);
                }
                psf->Release();
            }
            ILFree(pidlFirst);
        }
    }
    return hr;
}


// handle UI objects - for the most part we delegate to the real namespace implementation

HRESULT CSharedDocuments::s_FolderMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdo, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    CSharedDocuments *psd;
    psf->QueryInterface(CLSID_SharedDocuments, (void **)&psd);

    // defcm will only add the default handlers (eg. Open/Explore) if we have a callback
    // and the DFM_MERGECONTEXTMENU is successful.  so lets honor that so we can navigate

    if (uMsg == DFM_MERGECONTEXTMENU)
    {
        return S_OK;
    }
    else if (uMsg == DFM_INVOKECOMMAND)
    {
        HRESULT hr;
        DFMICS *pdfmics = (DFMICS *)lParam;
        switch (wParam)
        {
            case DFM_CMD_LINK:
                hr = SHCreateLinks(hwnd, NULL, pdo, SHCL_CONFIRM|SHCL_USETEMPLATE|SHCL_USEDESKTOP, NULL);                
                break;
            
            case DFM_CMD_PROPERTIES:
                hr = SHLaunchPropSheet(CFSFolder_PropertiesThread, pdo, (LPCTSTR)lParam, NULL, (void *)&c_idlDesktop);
                break;

            default:
                hr = S_FALSE;           // use the default handler for this item
                break;
        }
        return hr;
    }

    return E_NOTIMPL;
}

HRESULT CSharedDocuments::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST* apidl, REFIID riid, UINT* prgfInOut, void **ppv)
{
    if (cidl != 1)
        return E_FAIL;

    HRESULT hr = E_FAIL;
    if (IsEqualIID(riid, IID_IContextMenu))
    {
        // we must construct our own context menu for this item, we do this using the
        // shell default implementation and we pass it the information about a folder
        // that way we can navigate up and down through the namespace.

        IQueryAssociations *pqa;
        hr = GetUIObjectOf(hwnd, 1, apidl, IID_PPV_ARG_NULL(IQueryAssociations, &pqa));
        if (SUCCEEDED(hr))
        {
            // this is broken for docfiles (shell\ext\stgfldr's keys work though)
            // maybe because GetClassFile punts when it's not fs?

            HKEY ahk[MAX_ASSOC_KEYS];
            DWORD cKeys = SHGetAssocKeys(pqa, ahk, ARRAYSIZE(ahk));
            hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, this, 
                                        s_FolderMenuCB, 
                                        cKeys, ahk, 
                                        (IContextMenu **)ppv);
            SHRegCloseKeys(ahk, cKeys);
            pqa->Release();
        }
    }
    else if (IsEqualIID(riid, IID_IDataObject))
    {
        hr = SHCreateFileDataObject(_pidl, cidl, apidl, NULL, (IDataObject **)ppv);
    }
    else if (IsEqualIID(riid, IID_IQueryInfo))
    {
        IQueryAssociations *pqa;
        hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
        if (SUCCEEDED(hr))
        {
            WCHAR szCLSID[GUIDSTR_MAX];
            SHStringFromGUIDW(CLSID_SharedDocuments, szCLSID, ARRAYSIZE(szCLSID));
            hr = pqa->Init(0, szCLSID, NULL, NULL);
            if (SUCCEEDED(hr))
            {
                WCHAR szInfotip[INFOTIPSIZE];
                DWORD cchInfotip = ARRAYSIZE(szInfotip);
                hr = pqa->GetString(0, ASSOCSTR_INFOTIP, NULL, szInfotip, &cchInfotip);
                if (SUCCEEDED(hr))
                {
                    hr = CreateInfoTipFromText(szInfotip, IID_IQueryInfo, ppv); // _the_ InfoTip COM object
                }
            }
            pqa->Release();
        }
    }
    else if (IsEqualIID(riid, IID_IQueryAssociations))
    {
        LPITEMIDLIST pidlTarget;
        hr = _GetTarget(apidl[0], &pidlTarget);
        if (SUCCEEDED(hr))
        {
            hr = SHGetUIObjectOf(pidlTarget, hwnd, riid, ppv);
            ILFree(pidlTarget);
        }
    }
    else if (IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW))
    {
        UINT iIcon = II_FOLDER;
        UINT iIconOpen = II_FOLDEROPEN;

        TCHAR szModule[MAX_PATH];
        GetModuleFileName(HINST_THISDLL, szModule, ARRAYSIZE(szModule));

        hr = SHCreateDefExtIcon(szModule, iIcon, iIconOpen, GIL_PERCLASS, -1, riid, ppv);
    }
    else if (IsEqualIID(riid, IID_IDropTarget))
    {
        IShellFolder *psf;
        hr = _CreateFolder(NULL, *apidl, IID_PPV_ARG(IShellFolder, &psf), TRUE);
        if (SUCCEEDED(hr))
        {
            hr = psf->CreateViewObject(hwnd, riid, ppv);
            psf->Release();
        }
    }
    return hr;
}


HRESULT CSharedDocuments::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    HRESULT hr = ResultFromShort(0);

    // compare the contents of our IDLIST before we attemt to compare other elements
    // within it.

    LPCSHAREDITEM psid1 = (LPCSHAREDITEM)pidl1;
    LPCSHAREDITEM psid2 = (LPCSHAREDITEM)pidl2;

    if (psid1->dwType == psid2->dwType)
    {
        if (psid1->dwType == SHAREDID_USER)
        {
            hr = ResultFromShort(ualstrcmpi(psid1->wszID, psid2->wszID));
        }
        else
        {
            hr = ResultFromShort(0);            // common item == common item?    
        }
    }
    else
    {
        hr = ResultFromShort(psid1->dwType - psid2->dwType);
    }

    // if there was an exact match then lets compare the trailing elements of the IDLIST
    // if there are some (by binding down) etc.

    if (hr == ResultFromShort(0))
    {
        LPITEMIDLIST pidlNext1 = _ILNext(pidl1);
        LPITEMIDLIST pidlNext2 = _ILNext(pidl2);

        if (ILIsEmpty(pidlNext1))
        {
            if (ILIsEmpty(pidlNext2))
            {
                hr = ResultFromShort(0);    // pidl1 == pidl2 (in length)
            }
            else
            {
                hr = ResultFromShort(-1);   // pidl1 < pidl2 (in length)
            }
        }
        else
        {
            // if IDLIST2 is shorter then return > otherwise we should just
            // recurse down the IDLIST and let the next level compare.

            if (ILIsEmpty(pidlNext2))
            {
                hr = ResultFromShort(+1);   // pidl1 > pidl2 (in lenght)
            }
            else
            {
                LPITEMIDLIST pidlFirst = ILCloneFirst(pidl1);
                if (pidlFirst)
                {
                    IShellFolder *psf;
                    hr = _CreateFolder(NULL, pidlFirst, IID_PPV_ARG(IShellFolder, &psf), FALSE);
                    if (SUCCEEDED(hr))
                    {
                        hr = psf->CompareIDs(lParam, pidlNext1, pidlNext2);
                        psf->Release();    
                    }
                    ILFree(pidlFirst);
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                }
            }
        }
    }
    return hr;
}

HRESULT CSharedDocuments::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
    HRESULT hr = E_FAIL;
    if (IsEqualSCID(SCID_DESCRIPTIONID, *pscid))
    {
        SHDESCRIPTIONID did = {0};
        did.dwDescriptionId = SHDID_COMPUTER_SHAREDDOCS;
        did.clsid = CLSID_NULL;
        hr = InitVariantFromBuffer(pv, &did, sizeof(did));
    }
    else
    {
        LPITEMIDLIST pidlTarget;
        hr = _GetTarget(pidl, &pidlTarget);
        if (SUCCEEDED(hr))
        {
            IShellFolder2 *psf2;
            LPCITEMIDLIST pidlChild;
            hr = SHBindToIDListParent(pidlTarget, IID_PPV_ARG(IShellFolder2, &psf2), &pidlChild);
            if (SUCCEEDED(hr))
            {
                hr = psf2->GetDetailsEx(pidlChild, pscid, pv);
                psf2->Release();
            }
            ILFree(pidlTarget);
        }
    }
    return hr;
}


// icon overlay handling.   deligate this to the right handler

HRESULT CSharedDocuments::_GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex, BOOL fGetIconIndex)
{
    LPITEMIDLIST pidlTarget;
    HRESULT hr = _GetTarget(pidl, &pidlTarget);
    if (SUCCEEDED(hr))
    {
        IShellIconOverlay *psio;
        LPCITEMIDLIST pidlChild;
        hr = SHBindToIDListParent(pidlTarget, IID_PPV_ARG(IShellIconOverlay, &psio), &pidlChild);
        if (SUCCEEDED(hr))
        {   
            if (fGetIconIndex)
            {
                hr = psio->GetOverlayIconIndex(pidlChild, pIndex);
            }
            else
            {
                hr = psio->GetOverlayIndex(pidlChild, pIndex);
            }
            psio->Release();
        }
        ILFree(pidlTarget);
    }
    return hr;
}


// enumerator for listing all the shared documents in the system.

class CSharedDocsEnum : public IEnumIDList
{
private:
    LONG _cRef;
    HDPA _hItems;
    DWORD _grfFlags;
    int _index;

public:
    CSharedDocsEnum(HDPA hItems, DWORD grf);
    ~CSharedDocsEnum();

    // IUnknown
    STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    // IEnumIDList
    STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
    STDMETHODIMP Skip(ULONG celt) 
        { return E_NOTIMPL; }
    STDMETHODIMP Reset()    
        { _index = 0; return S_OK; }
    STDMETHODIMP Clone(IEnumIDList **ppenum) 
        { return E_NOTIMPL; };

};

CSharedDocsEnum::CSharedDocsEnum(HDPA hItems, DWORD grfFlags) :
    _cRef(1),
    _hItems(hItems),
    _grfFlags(grfFlags),
    _index(0)
{
}

CSharedDocsEnum::~CSharedDocsEnum()
{
    DPA_FreeIDArray(_hItems);
}

HRESULT CSharedDocsEnum_CreateInstance(HDPA hItems, DWORD grfFlags, IEnumIDList **ppenum)
{
    CSharedDocsEnum *penum = new CSharedDocsEnum(hItems, grfFlags);
    if (!penum)
        return E_OUTOFMEMORY;

    HRESULT hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
    penum->Release();
    return hr;
}


// IUnknown handling

STDMETHODIMP CSharedDocsEnum::QueryInterface(REFIID riid, void **ppv)
{
    static const QITAB qit[] = 
    {
        QITABENT(CSharedDocsEnum, IEnumIDList),                              // IID_IEnumIDList
        { 0 },
    };
    return QISearch(this, qit, riid, ppv);
}

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

STDMETHODIMP_(ULONG) CSharedDocsEnum::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;
            
    delete this;
    return 0;
}


// enumeration handling

HRESULT CSharedDocsEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
{   
    HRESULT hr = S_FALSE;
    ULONG cFetched = 0;

    if (_grfFlags & SHCONTF_FOLDERS)
    {
        // if we have more items to return and the buffer is still not full
        // then lets ensure that we return them.

        while (SUCCEEDED(hr) && (celt != cFetched) && (_index != DPA_GetPtrCount(_hItems)))
        {
            if (_index != DPA_GetPtrCount(_hItems))
            {
                hr = SHILClone((LPITEMIDLIST)DPA_GetPtr(_hItems, _index), &rgelt[cFetched]);
                if (SUCCEEDED(hr))
                {
                    cFetched++;       
                }
            }
            _index++;
        }
    }

    if (pceltFetched)
        *pceltFetched = cFetched;

    return hr;
}


// handle system initialization of the shared documents objects

void _SetLocalizedName(INT csidl, LPTSTR pszResModule, INT idsRes)
{
    TCHAR szPath[MAX_PATH];
    if (SHGetSpecialFolderPath(NULL, szPath, csidl, TRUE))
    {
        SHSetLocalizedName(szPath, pszResModule, idsRes);
    }
}

HRESULT SHGetSampleMediaFolder(int nAllUsersMediaFolder, LPITEMIDLIST *ppidlSampleMedia);
#define PICTURES_BUYURL L"SamplePictures"
#define SAMPLEMUSIC_BUYURL L"http://windowsmedia.com/redir/xpsample.asp"

STDAPI_(void) InitializeSharedDocs(BOOL fWow64)
{
    // ACL the DocFolder paths key so that users can touch the keys and store their paths
    // for the document folders they have. 

    // we want the "Everyone" to have read/write access
    SHELL_USER_PERMISSION supEveryone;
    supEveryone.susID = susEveryone;
    supEveryone.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
    supEveryone.dwAccessMask = KEY_READ|KEY_WRITE;
    supEveryone.fInherit = TRUE;
    supEveryone.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
    supEveryone.dwInheritAccessMask = GENERIC_READ;

    // we want the "SYSTEM" to have full control
    SHELL_USER_PERMISSION supSystem;
    supSystem.susID = susSystem;
    supSystem.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
    supSystem.dwAccessMask = KEY_ALL_ACCESS;
    supSystem.fInherit = TRUE;
    supSystem.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
    supSystem.dwInheritAccessMask = GENERIC_ALL;

    // we want the "Administrators" to have full control
    SHELL_USER_PERMISSION supAdministrators;
    supAdministrators.susID = susAdministrators;
    supAdministrators.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
    supAdministrators.dwAccessMask = KEY_ALL_ACCESS;
    supAdministrators.fInherit = TRUE;
    supAdministrators.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
    supAdministrators.dwInheritAccessMask = GENERIC_ALL;

    PSHELL_USER_PERMISSION aPerms[3] = {&supEveryone, &supSystem, &supAdministrators};
    SECURITY_DESCRIPTOR* psd = GetShellSecurityDescriptor(aPerms, ARRAYSIZE(aPerms));
    if (psd)
    {
        HKEY hk;
        if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DocFolderPaths"), 0, NULL, REG_OPTION_NON_VOLATILE, MAXIMUM_ALLOWED, NULL, &hk, NULL) == ERROR_SUCCESS)
        {
            RegSetKeySecurity(hk, DACL_SECURITY_INFORMATION, psd);
            RegCloseKey(hk);
        }
        LocalFree(psd);
    }
 
    // do file system initialization as needed so that the shared music/pictures folders
    // have the correct display names.

    if (!fWow64)
    {   
        _SetLocalizedName(CSIDL_COMMON_PICTURES, TEXT("shell32.dll"), IDS_SHAREDPICTURES);    
        _SetLocalizedName(CSIDL_COMMON_MUSIC, TEXT("shell32.dll"), IDS_SHAREDMUSIC);

        // Set the Sample Pictures buy URL
        LPITEMIDLIST pidl;
        if (SUCCEEDED(SHGetSampleMediaFolder(CSIDL_COMMON_PICTURES, &pidl)))
        {
            WCHAR szPath[MAX_PATH];
            WCHAR szDesktopIni[MAX_PATH];
            if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath)) && PathCombine(szDesktopIni, szPath, L"desktop.ini"))
            {
                WritePrivateProfileString(L".ShellClassInfo", c_BuySamplePictures.szURLKey, PICTURES_BUYURL, szDesktopIni);

                // Ensure this is a system folder
                PathMakeSystemFolder(szPath);
            }

            ILFree(pidl);
        }

        // Set the Sample Music buy URL
        if (SUCCEEDED(SHGetSampleMediaFolder(CSIDL_COMMON_MUSIC, &pidl)))
        {
            WCHAR szPath[MAX_PATH];
            WCHAR szDesktopIni[MAX_PATH];
            if (SUCCEEDED(SHGetPathFromIDList(pidl, szPath)) && PathCombine(szDesktopIni, szPath, L"desktop.ini"))
            {
                WritePrivateProfileString(L".ShellClassInfo", c_BuySampleMusic.szURLKey, SAMPLEMUSIC_BUYURL, szDesktopIni);

                // Ensure this is a system folder
                PathMakeSystemFolder(szPath);
            }

            ILFree(pidl);
        }
    }
}