//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1997.
//
//  File:       I S H E L L F . C P P
//
//  Contents:   IShellFolder implementation for CConnectionFolder
//
//  Notes:      The IShellFolder interface is used to manage folders within
//              the namespace. Objects that support IShellFolder are
//              usually created by other shell folder objects, with the root
//              object (the Desktop shell folder) being returned from the
//              SHGetDesktopFolder function.
//
//  Author:     jeffspr   22 Sep 1997
//
//----------------------------------------------------------------------------


#include "pch.h"
#pragma hdrstop

#include "foldinc.h"    // Standard shell\folder includes
#include "cfutils.h"    // Connections folder utilities
#include "foldres.h"
#include "ncnetcon.h"
#include "droptarget.h"
#include "ncperms.h"
#include "ncras.h"
#include "cmdtable.h"
#include "webview.h"

#define ENABLE_CONNECTION_TOOLTIP

const WCHAR c_szNetworkConnections[] = L"NetworkConnections";

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::ParseDisplayName
//
//  Purpose:    Translates a file object or folder's display name into an
//              item identifier.
//
//  Arguments:
//      hwndOwner       [in]    Handle of owner window
//      pbcReserved     [in]    Reserved
//      lpszDisplayName [in]    Pointer to diplay name
//      pchEaten        [out]   Pointer to value for parsed characters
//      ppidl           [out]   Pointer to new item identifier list
//      pdwAttributes   [out]   Address receiving attributes of file object
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise
//
//  Author:     jeffspr   18 Oct 1997

STDMETHODIMP CConnectionFolder::ParseDisplayName(
    HWND            hwndOwner,
    LPBC            pbcReserved,
    LPOLESTR        lpszDisplayName,
    ULONG *         pchEaten,
    LPITEMIDLIST *  ppidl,
    ULONG *         pdwAttributes)
{
    HRESULT hr = S_OK;
    TraceFileFunc(ttidShellFolder);

    if (!ppidl)
    {
        return E_POINTER;
    }

    *ppidl = NULL;

    if ((lpszDisplayName == NULL) ||
        (wcslen(lpszDisplayName) < (c_cchGuidWithTerm - 1)))
    {
        return E_INVALIDARG;
    }

    while (*lpszDisplayName == ':')
    {
        lpszDisplayName++;
    }

    if (*lpszDisplayName != '{')
    {
        return E_INVALIDARG;
    }

    GUID guid;

    if (SUCCEEDED(CLSIDFromString(lpszDisplayName, &guid)))
    {
        if (g_ccl.IsInitialized() == FALSE)
        {
            g_ccl.HrRefreshConManEntries();
        }

        PCONFOLDPIDL pidl;
        hr = g_ccl.HrFindPidlByGuid(&guid, pidl);
        if (S_OK == hr)
        {
            *ppidl = pidl.TearOffItemIdList();
            TraceTag(ttidShellFolderIface, "IShellFolder::ParseDisplayName generated PIDL: 0x%08x", *ppidl);
        }
        else
        {
            hr = E_FILE_NOT_FOUND;
        }
    }
    else
    {
        return(E_FAIL);
    }

    if (SUCCEEDED(hr) && pdwAttributes)
    {   
        LPCITEMIDLIST pidlArr[1];
        pidlArr[0] = *ppidl;
        hr = GetAttributesOf(1, pidlArr, pdwAttributes);
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::EnumObjects
//
//  Purpose:    Determines the contents of a folder by creating an item
//              enumeration object (a set of item identifiers) that can be
//              retrieved using the IEnumIDList interface.
//
//  Arguments:
//      hwndOwner    [in]   Handle of owner window
//      grfFlags     [in]   Items to include in enumeration
//      ppenumIDList [out]  Pointer to IEnumIDList
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::EnumObjects(
    HWND            hwndOwner,
    DWORD           grfFlags,
    LPENUMIDLIST *  ppenumIDList)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT hr  = NOERROR;

    Assert(ppenumIDList);

    NETCFG_TRY
        // Create the IEnumIDList object (CConnectionFolderEnum)
        //
        hr = CConnectionFolderEnum::CreateInstance (
                IID_IEnumIDList,
                reinterpret_cast<void**>(ppenumIDList));

        if (SUCCEEDED(hr))
        {
            Assert(*ppenumIDList);

            // Call the PidlInitialize function to allow the enumeration
            // object to copy the list.
            //
            reinterpret_cast<CConnectionFolderEnum *>(*ppenumIDList)->PidlInitialize(
                FALSE, m_pidlFolderRoot, m_dwEnumerationType);

        }
        else
        {
            // On all failures, this should be NULL.
            if (*ppenumIDList)
            {
                ReleaseObj(*ppenumIDList);
            }

            *ppenumIDList = NULL;
        }

    NETCFG_CATCH(hr)
        
    TraceHr(ttidError, FAL, hr, FALSE, "CConnectionFolder::EnumObjects");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::BindToObject
//
//  Purpose:    Creates an IShellFolder object for a subfolder.
//
//  Arguments:
//      pidl        [in]    Pointer to an ITEMIDLIST
//      pbcReserved [in]    Reserved - specify NULL
//      riid        [in]    Interface to return
//      ppvOut      [out]   Address that receives interface pointer;
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:      We don't need this function, since we don't have subfolders.
//
STDMETHODIMP CConnectionFolder::BindToObject(
    LPCITEMIDLIST   pidl,
    LPBC            pbcReserved,
    REFIID          riid,
    LPVOID *        ppvOut)
{
    TraceFileFunc(ttidShellFolder);

    // Note - If we add code here, then we ought to param check pidl
    //
    Assert(pidl);

    *ppvOut = NULL;

    return E_NOTIMPL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::BindToStorage
//
//  Purpose:    Reserved for a future use. This method should
//              return E_NOTIMPL.
//
//  Arguments:
//      pidl        []  Pointer to an ITEMIDLIST
//      pbcReserved []  Reserved�specify NULL
//      riid        []  Interface to return
//      ppvObj      []  Address that receives interface pointer);
//
//  Returns:    E_NOTIMPL always
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::BindToStorage(
    LPCITEMIDLIST   pidl,
    LPBC            pbcReserved,
    REFIID          riid,
    LPVOID *        ppvObj)
{
    TraceFileFunc(ttidShellFolder);

    // Note - If we add code here, then we ought to param check pidl
    //
    Assert(pidl);

    *ppvObj = NULL;

    return E_NOTIMPL;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::CompareIDs
//
//  Purpose:    Determines the relative ordering of two file objects or
//              folders, given their item identifier lists.
//
//  Arguments:
//      lParam [in]     Type of comparison to perform
//      pidl1  [in]     Address of ITEMIDLIST structure
//      pidl2  [in]     Address of ITEMIDLIST structure
//
//  Returns:    Returns a handle to a result code. If this method is
//              successful, the CODE field of the status code (SCODE) has
//              the following meaning:
//
//              CODE field          Meaning
//              ----------          -------
//              Less than zero      The first item should precede the second
//                                  (pidl1 < pidl2).
//              Greater than zero   The first item should follow the second
//                                  (pidl1 > pidl2)
//              Zero                The two items are the same (pidl1 = pidl2)
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:      Passing 0 as the lParam indicates sort by name.
//              0x00000001-0x7fffffff are for folder specific sorting rules.
//              0x80000000-0xfffffff are used the system.
//
STDMETHODIMP CConnectionFolder::CompareIDs(
    LPARAM          lParam,
    LPCITEMIDLIST   pidl1,
    LPCITEMIDLIST   pidl2)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT         hr          = S_OK;
    int             iCompare    = 0;
    CONFOLDENTRY    pccfe1;
    CONFOLDENTRY    pccfe2;
    PCONFOLDPIDL    pcfp1;
    PCONFOLDPIDL    pcfp2;
    ConnListEntry   cle1;
    ConnListEntry   cle2;
    PCWSTR          pszString1  = NULL;
    PCWSTR          pszString2  = NULL;
    INT             iStringID1  = 0;
    INT             iStringID2  = 0;

    hr = pcfp1.InitializeFromItemIDList(pidl1);
    if (SUCCEEDED(hr))
    {
        hr = pcfp2.InitializeFromItemIDList(pidl2);
    }
    
    // Make sure that the pidls passed in are our pidls.
    //
    if (FAILED(hr))
    {
        hr = E_INVALIDARG;
        goto Exit;
    }

    if (WIZARD_NOT_WIZARD != pcfp1->wizWizard && WIZARD_NOT_WIZARD != pcfp2->wizWizard)
    {
        hr = ResultFromShort(0);

        if (pcfp1->wizWizard > pcfp2->wizWizard)
            hr = ResultFromShort(-1);

        if (pcfp1->wizWizard < pcfp2->wizWizard)
            hr = ResultFromShort(1);

        goto Exit;
    }

    // If the first item is a wizard, then it comes first.
    //
    if (WIZARD_NOT_WIZARD != pcfp1->wizWizard)
    {
        hr = ResultFromShort(-1);
        goto Exit;
    }

    // If the second item is a wizard, then, well, you get the picture.
    //
    if (WIZARD_NOT_WIZARD != pcfp2->wizWizard)
    {
        hr = ResultFromShort(1);
        goto Exit;
    }

    // Note: (jeffspr) & SHC... should be removed once Victor Tan checks in a fix 
    // for the IShellFolder2 params being used in IShellFolder
    //
    switch(lParam & SHCIDS_COLUMNMASK)
    {
        case ICOL_NAME:
            {
                // Check the name. If the name is the same, then we need to
                // check the GUID as well, because we HAVE TO allow duplicate names,
                // and this function is used to uniquely identify connections for
                // notification purposes
                //
                LPCWSTR szPcfpName1 = pcfp1->PszGetNamePointer() ? pcfp1->PszGetNamePointer() : L"\0";
                LPCWSTR szPcfpName2 = pcfp2->PszGetNamePointer() ? pcfp2->PszGetNamePointer() : L"\0";

                iCompare = lstrcmpW(szPcfpName1, szPcfpName2);
                if (iCompare == 0)
                {
                    if (!InlineIsEqualGUID(pcfp1->guidId, pcfp2->guidId))
                    {
                        // Doesn't really matter which order we put them
                        // in, as long as we call them non-equal
                        iCompare = -1;
                    }
                }
            }
            break;

        case ICOL_TYPE:
            {
                MapNCMToResourceId(pcfp1->ncm, pcfp1->dwCharacteristics, &iStringID1);
                MapNCMToResourceId(pcfp2->ncm, pcfp2->dwCharacteristics, &iStringID2);
                pszString1 = (PWSTR) SzLoadIds(iStringID1);
                pszString2 = (PWSTR) SzLoadIds(iStringID2);
                if (pszString1 && pszString2)
                {
                    iCompare = lstrcmpW(pszString1, pszString2);
                }
            }
            break;

        case ICOL_STATUS:
            {
                WCHAR szString1[CONFOLD_MAX_STATUS_LENGTH];
                WCHAR szString2[CONFOLD_MAX_STATUS_LENGTH];
                MapNCSToComplexStatus(pcfp1->ncs, pcfp1->ncm, pcfp1->ncsm, pcfp1->dwCharacteristics, szString1, CONFOLD_MAX_STATUS_LENGTH, pcfp1->guidId);
                MapNCSToComplexStatus(pcfp2->ncs, pcfp2->ncm, pcfp1->ncsm, pcfp2->dwCharacteristics, szString2, CONFOLD_MAX_STATUS_LENGTH, pcfp2->guidId);
                iCompare = lstrcmpW(szString1, szString2);
            }
            break;

        case ICOL_DEVICE_NAME:
            {
                LPCWSTR szPcfpDeviceName1 = pcfp1->PszGetDeviceNamePointer() ? pcfp1->PszGetDeviceNamePointer() : L"\0";
                LPCWSTR szPcfpDeviceName2 = pcfp2->PszGetDeviceNamePointer() ? pcfp2->PszGetDeviceNamePointer() : L"\0";
                iCompare = lstrcmpW(szPcfpDeviceName1, szPcfpDeviceName2);
            }
            break;

        case ICOL_OWNER:
            {
                pszString1 = PszGetOwnerStringFromCharacteristics(pszGetUserName(), pcfp1->dwCharacteristics);
                pszString2 = PszGetOwnerStringFromCharacteristics(pszGetUserName(), pcfp2->dwCharacteristics);
                iCompare = lstrcmpW(pszString1, pszString2);
            }
            break;

        case ICOL_PHONEORHOSTADDRESS:
            {
                LPCWSTR szPcfpPhoneHostAddress1 = pcfp1->PszGetPhoneOrHostAddressPointer() ? pcfp1->PszGetPhoneOrHostAddressPointer() : L"\0";
                LPCWSTR szPcfpPhoneHostAddress2 = pcfp2->PszGetPhoneOrHostAddressPointer() ? pcfp2->PszGetPhoneOrHostAddressPointer() : L"\0";
                iCompare = lstrcmpW(szPcfpPhoneHostAddress1, szPcfpPhoneHostAddress2);
            }
            break;

        default:
//            AssertFmt(FALSE, FAL, "Shell bug - Sorting on unknown category. Column = %x", (lParam & SHCIDS_COLUMNMASK));
            hr = E_INVALIDARG;
            break;
    }


    if (SUCCEEDED(hr))
    {
        hr = ResultFromShort(iCompare);
    }

Exit:
    // If these were allocated instead of cached, delete them
    //
    TraceHr(ttidError, FAL, hr,
            (ResultFromShort(-1) == hr) || (ResultFromShort(1) == hr),
            "CConnectionFolder::CompareIDs");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::CreateViewObject
//
//  Purpose:    Creates a view object of a folder.
//
//  Arguments:
//      hwndOwner [in]      Handle of owner window
//      riid      [in]      Interface identifier
//      ppvOut    [none]    Reserved
//
//  Returns:    Returns NOERROR if successful or an OLE defined error
//              value otherwise.
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::CreateViewObject(
    HWND        hwndOwner,
    REFIID      riid,
    LPVOID *    ppvOut)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT hr  = E_NOINTERFACE;

    Assert(ppvOut);
    Assert(this);

    // Pre-initialize the out param, per OLE guidelines
    //
    *ppvOut = NULL;

    if (riid == IID_IShellView)
    {
        if (FHasPermission(NCPERM_OpenConnectionsFolder))
        {
            SFV_CREATE sfv = {0};
            sfv.cbSize         = sizeof(sfv);
            sfv.pshf           = dynamic_cast<IShellFolder2*>(this);
            sfv.psfvcb         = dynamic_cast<IShellFolderViewCB*>(this);

            // Note: The shell never gets around to freeing the last view
            //          when shutting down...
            //
            hr = SHCreateShellFolderView(&sfv, &m_pShellView);
            if (SUCCEEDED(hr))
            {
                *ppvOut = m_pShellView;
                DWORD   dwErr   = 0;

                // Get the state of the "ManualDial" flag from RAS
                // so we can initialize our global
                //
                dwErr = RasUserGetManualDial(
                    hwndOwner,
                    FALSE,
                    (PBOOL) (&g_fOperatorAssistEnabled));

                // Ignore the error (don't shove it in the Hr), because
                // we still want to run even if we failed to get the value
                // Trace it, though
                Assert(dwErr == 0);
                TraceHr(ttidShellFolder, FAL, HRESULT_FROM_WIN32(dwErr), FALSE,
                        "RasUserGetManualDial call from CreateViewObject");
            }
        }
        else
        {
            TraceTag(ttidShellFolder, "No permission to open connections folder (FHasPermission returned 0)");
            AssertSz(FALSE, "get off!");

            if (hwndOwner)
            {
                NcMsgBox(_Module.GetResourceInstance(), hwndOwner,
                    IDS_CONFOLD_WARNING_CAPTION,
                    IDS_CONFOLD_NO_PERMISSIONS_FOR_OPEN,
                    MB_ICONEXCLAMATION | MB_OK);

                hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);   // user saw the error
            }
            else
            {
                hr = E_ACCESSDENIED;
            }
        }
    }
    else if (riid == IID_IContextMenu)
    {
        // Create our context menu object for the background CMs.
        //
        hr = CConnectionFolderContextMenu::CreateInstance (
                IID_IContextMenu,
                reinterpret_cast<void**>(ppvOut),
                CMT_BACKGROUND,
                hwndOwner,
                PCONFOLDPIDLVEC(NULL),
                this);
        if (SUCCEEDED(hr))
        {
            Assert(*ppvOut);
        }
     }
     else if (riid == IID_ICategoryProvider)
     {
         // Create our context menu object for the background CMs.
         //
         
         CComPtr<IDefCategoryProvider> pDevCategoryProvider;
         hr = CoCreateInstance(CLSID_DefCategoryProvider, NULL, CLSCTX_ALL, IID_IDefCategoryProvider, reinterpret_cast<LPVOID *>(&pDevCategoryProvider));
         if (SUCCEEDED(hr))
         {
             
             SHCOLUMNID pscidType, pscidPhoneOrHostAddress;
             MapColumnToSCID(ICOL_TYPE, &pscidType);
             MapColumnToSCID(ICOL_PHONEORHOSTADDRESS, &pscidPhoneOrHostAddress);
             
             SHCOLUMNID pscidExclude[2];
             pscidExclude[0].fmtid = GUID_NETSHELL_PROPS;
             pscidExclude[0].pid   = ICOL_PHONEORHOSTADDRESS;
             
             pscidExclude[1].fmtid = GUID_NULL;
             pscidExclude[1].pid   = 0;
             
             CATLIST catList[] = 
             {
                 {&GUID_NULL, NULL}
             };
             
             if (SUCCEEDED(hr))
             {
                 pDevCategoryProvider->Initialize(&GUID_NETSHELL_PROPS,
                     &pscidType,
                     pscidExclude,
                     NULL,
                     catList,
                     this);
                 
                 hr = pDevCategoryProvider->QueryInterface(IID_ICategoryProvider, ppvOut);
             }
         }
     }
     else
     {
         goto Exit;
     }

Exit:

    TraceHr(ttidError, FAL, hr, (E_NOINTERFACE == hr),
            "CConnectionFolder::CreateViewObject");

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::GetAttributesOf
//
//  Purpose:    Retrieves the attributes that all passed-in objects (file
//              objects or subfolders) have in common.
//
//  Arguments:
//      cidl     [in]   Number of file objects
//      apidl    [in]   Pointer to array of pointers to ITEMIDLIST structures
//      rgfInOut [out]  Address of value containing attributes of the
//                      file objects
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise.
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::GetAttributesOf(
    UINT            cidl,
    LPCITEMIDLIST * apidl,
    ULONG *         rgfInOut)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT         hr              = S_OK;
    ULONG           rgfMask         = 0;
    PCONFOLDPIDL    pcfp;

    if (cidl > 0)
    {
        PCONFOLDPIDLVEC pcfpVec;
        hr = PConfoldPidlVecFromItemIdListArray(apidl, cidl, pcfpVec);
        if (FAILED(hr))
        {
            return E_INVALIDARG;
        }
        
        // Prepopulate with all values (removed CANCOPY and CANMOVE)
        //
        rgfMask =   SFGAO_CANDELETE |
                    SFGAO_CANRENAME     |
                    SFGAO_CANLINK       |
                    SFGAO_HASPROPSHEET;

        // Disable propsheets for > 1 connection
        //
        if (cidl > 1)
        {
            rgfMask &= ~SFGAO_HASPROPSHEET;
        }

        PCONFOLDPIDLVEC::const_iterator iterLoop;
        for (iterLoop = pcfpVec.begin(); iterLoop != pcfpVec.end(); iterLoop++)
        {
            // Translate the PIDL to our struct, and check for wizard inclusion.
            // If so, then we don't support anything but "link". If not, then
            // we support all of the standard actions

            const PCONFOLDPIDL& pcfp = *iterLoop;
            if(!pcfp.empty())
            {
                if (((*rgfInOut) & SFGAO_VALIDATE))
                {
                    ConnListEntry cleDontCare;
                    hr = g_ccl.HrFindConnectionByGuid(&(pcfp->guidId), cleDontCare);
                    if (hr != S_OK)
                    {
                        // Note: Remove this when we get RAS notifications, because
                        // we will ALWAYS have the information we need to find the connections
                        // We're doing this because the CM folks are creating RAS icons on the
                        // desktop without us knowing about it.
                        //
                        // If we didn't find it, then flush the cache and try again.
                        //
                        if (S_FALSE == hr)
                        {
                            hr = g_ccl.HrRefreshConManEntries();
                            if (SUCCEEDED(hr))
                            {
                                hr = g_ccl.HrFindConnectionByGuid(&(pcfp->guidId), cleDontCare);
                                if (hr != S_OK)
                                {
                                    hr = E_FAIL;
                                    goto Exit;
                                }
                            }
                        }
                        else
                        {
                            hr = E_FAIL;
                            goto Exit;
                        }
                    }
                }

                if (WIZARD_NOT_WIZARD != pcfp->wizWizard)
                {
                    // No support for delete/rename/etc, since it's the wizard.
                    // However, we want to provide our own "delete" warning when the
                    // wizard is selected along with deleteable connections
                    //
                    rgfMask = SFGAO_CANLINK | SFGAO_CANDELETE;
                }

                if (pcfp->dwCharacteristics & NCCF_BRANDED)
                {
                    if ( !fIsConnectedStatus(pcfp->ncs) && (pcfp->ncs != NCS_DISCONNECTING) )
                    {
                        rgfMask |= SFGAO_GHOSTED;
                    }
                }

                if (pcfp->dwCharacteristics & NCCF_INCOMING_ONLY)
                {
                    rgfMask &= ~SFGAO_CANLINK;
                }

                // Mask out the unavailable attributes for this connection
                //
                if (!(pcfp->dwCharacteristics & NCCF_ALLOW_RENAME) || !HasPermissionToRenameConnection(pcfp))
                {
                    rgfMask &= ~SFGAO_CANRENAME;
                }

    #if 0   // If I mask this out, I can't give user feedback for objects that can't be deleted.
                if (pcfp->dwCharacteristics & NCCF_ALLOW_REMOVAL)
                {
                    rgfMask |= SFGAO_CANDELETE;
                }
    #endif
            }
        }
    }
    else
    {
        // Apparently, we're called with 0 objects to indicate that we're
        // supposed to return flags for the folder itself, not an individual
        // object. Weird.
        rgfMask = SFGAO_CANCOPY   |
                  SFGAO_CANDELETE |
                  SFGAO_CANMOVE   |
                  SFGAO_CANRENAME |
                  SFGAO_DROPTARGET;
    }

Exit:
    if (SUCCEEDED(hr))
    {
        *rgfInOut &= rgfMask;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::GetUIObjectOf
//
//  Purpose:    Creates a COM object that can be used to carry out actions
//              on the specified file objects or folders, typically, to
//              create context menus or carry out drag-and-drop operations.
//
//  Arguments:
//      hwndOwner [in]      Handle to owner window
//      cidl      [in]      Number of objects specified in apidl
//      apidl     [in]      Pointer to an array of pointers to an ITEMIDLIST
//      riid      [in]      Interface to return
//      prgfInOut [none]    Reserved
//      ppvOut    [out]     Address to receive interface pointer
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::GetUIObjectOf(
    HWND            hwndOwner,
    UINT            cidl,
    LPCITEMIDLIST * apidl,
    REFIID          riid,
    UINT *          prgfInOut,
    LPVOID *        ppvOut)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT hr  = E_NOINTERFACE;

    NETCFG_TRY
        
        if (cidl >= 1)
        {
            Assert(apidl);
            Assert(apidl[0]);
            Assert(ppvOut);

            if (riid == IID_IDataObject)
            {
                // Need to initialize so the SUCCEEED check below doesn't fail.
                //
                hr = S_OK;

                if (m_pidlFolderRoot.empty())
                {
                    hr = HrGetConnectionsFolderPidl(m_pidlFolderRoot);
                }
                
                if (SUCCEEDED(hr))
                {
                    Assert(!m_pidlFolderRoot.empty());

                    // Internal IDataObject impl removed. Replaced with common
                    // shell code.
                    //
                    hr = CIDLData_CreateFromIDArray(m_pidlFolderRoot.GetItemIdList(), cidl, apidl, (IDataObject **) ppvOut);
                }
            }
            else if (riid == IID_IContextMenu)
            {
                PCONFOLDPIDLVEC pcfpVec;
                hr = PConfoldPidlVecFromItemIdListArray(apidl, cidl, pcfpVec);
                if (FAILED(hr))
                {
                    return E_INVALIDARG;
                }
                
                // Create our context menu object for the background CMs.
                //
                if (SUCCEEDED(hr))
                {
                    hr = CConnectionFolderContextMenu::CreateInstance (
                            IID_IContextMenu,
                            reinterpret_cast<void**>(ppvOut),
                            CMT_OBJECT,
                            hwndOwner,
                            pcfpVec,
                            this);
                    if (SUCCEEDED(hr))
                    {
                        Assert(*ppvOut);
                    }
                    else
                    {
                        hr = E_NOINTERFACE;
                    }
                }
            }
            else if (riid == IID_IExtractIconA || riid == IID_IExtractIconW)
            {
                if (cidl == 1)
                {
                    hr = CConnectionFolderExtractIcon::CreateInstance (
                            apidl[0],
                            riid,
                            reinterpret_cast<void**>(ppvOut));

                    if (SUCCEEDED(hr))
                    {
                        Assert(*ppvOut);
                    }
                }
                else
                {
                    hr = E_NOINTERFACE;
                }
            }
            else if (riid == IID_IDropTarget)
            {
                hr = E_NOINTERFACE;
            }
            else if (riid == IID_IQueryAssociations)
            {
                CComPtr<IQueryAssociations> pQueryAssociations;

                hr = AssocCreate(CLSID_QueryAssociations, IID_IQueryAssociations, reinterpret_cast<LPVOID *>(&pQueryAssociations));
                if (SUCCEEDED(hr))
                {
                    hr = pQueryAssociations->Init(0, c_szNetworkConnections, NULL, NULL);
                    if (SUCCEEDED(hr))
                    {
                        hr = pQueryAssociations->QueryInterface(IID_IQueryAssociations, ppvOut);
                    }
                }
            }
            else if (riid == IID_IQueryInfo)
            {
    #ifdef ENABLE_CONNECTION_TOOLTIP
                if (cidl == 1)
                {
                    PCONFOLDPIDLVEC pcfpVec;
                    hr = PConfoldPidlVecFromItemIdListArray(apidl, cidl, pcfpVec);
                    if (FAILED(hr))
                    {
                        return E_INVALIDARG;
                    }
                    
                    const PCONFOLDPIDL& pcfp = *pcfpVec.begin();

                    // Create the IQueryInfo interface
                    hr = CConnectionFolderQueryInfo::CreateInstance (
                            IID_IQueryInfo,
                            reinterpret_cast<void**>(ppvOut));

                    if (SUCCEEDED(hr))
                    {
                        Assert(*ppvOut);

                        reinterpret_cast<CConnectionFolderQueryInfo *>
                            (*ppvOut)->PidlInitialize(*pcfpVec.begin());

                        // Normalize return code
                        //
                        hr = NOERROR;
                    }
                }
                else
                {
                    AssertSz(FALSE, "GetUIObjectOf asked for query info for more than one item!");
                    hr = E_NOINTERFACE;
                }
    #else
                hr = E_NOINTERFACE;
    #endif // ENABLE_CONNECTION_TOOLTIP

            }
            else
            {
                TraceTag(ttidShellFolder, "CConnectionFolder::GetUIObjectOf asked for object "
                         "that it didn't know how to create. 0x%08x", riid.Data1);

                hr = E_NOINTERFACE;
            }
        }

        if (FAILED(hr))
        {
            *ppvOut = NULL;
        }

    NETCFG_CATCH(hr)
        
    TraceHr(ttidError, FAL, hr, (hr == E_NOINTERFACE), "CConnectionFolder::GetUIObjectOf");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::GetDisplayNameOf
//
//  Purpose:    Retrieves the display name for the specified file object or
//              subfolder, returning it in a STRRET structure.
//
//  Arguments:
//      pidl   [in]     Pointer to an ITEMIDLIST
//      uFlags [in]     Type of display to return
//      lpName [out]    Pointer to a STRRET structure
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise.
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::GetDisplayNameOf(
    LPCITEMIDLIST   pidl,
    DWORD           uFlags,
    LPSTRRET        lpName)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT         hr              = S_OK;
    PWSTR           pszStrToCopy    = NULL;

    Assert(lpName);

    if (!pidl || !lpName)
    {
        return E_INVALIDARG;
    }

    PCONFOLDPIDL   pcfpLatestVersion;
    PCONFOLDPIDL   pcfpLatestVersionCached;
    PCONFOLDPIDL98 pcfp98;

    CONFOLDPIDLTYPE cfpt = GetPidlType(pidl);
    switch (cfpt)
    {
        case PIDL_TYPE_V1:
        case PIDL_TYPE_V2:
            if (FAILED(pcfpLatestVersion.InitializeFromItemIDList(pidl)))
            {
               return E_INVALIDARG;
            }
            break;
        case PIDL_TYPE_98: 
            if (FAILED(pcfp98.InitializeFromItemIDList(pidl)))
            {
                return E_INVALIDARG;
            }
            break;

        default:
            AssertSz(FALSE, "CConnectionFolder::GetDisplayNameOf - Invalid PIDL");
            return E_INVALIDARG;
            break;
    }

    if ( (PIDL_TYPE_V1 == cfpt) || (PIDL_TYPE_V2 == cfpt) )
    {
    #ifdef DBG
        // Throw these in here just so I can quickly peek at the values
        // set while I'm dorking around in the debugger.
        //
        DWORD   dwInFolder          = (uFlags & SHGDN_INFOLDER);
        DWORD   dwForAddressBar     = (uFlags & SHGDN_FORADDRESSBAR);
        DWORD   dwForParsing        = (uFlags & SHGDN_FORPARSING);
    #endif

        // Find the correct string for the display name. For the wizard, we get it
        // from the resources. Otherwise, we use the actual connection name
        //
        lpName->uType = STRRET_WSTR;

        if (uFlags & SHGDN_FORPARSING)
        {
            lpName->pOleStr = (LPWSTR)SHAlloc(c_cbGuidWithTerm);

            if (lpName->pOleStr == NULL)
            {
                return(ERROR_NOT_ENOUGH_MEMORY);
            }

            if (StringFromGUID2(pcfpLatestVersion->clsid, lpName->pOleStr, c_cbGuidWithTerm) == 0)
            {
                return(ERROR_INVALID_NAME);
            }

            return(S_OK);
        }
        else if (WIZARD_MNC == pcfpLatestVersion->wizWizard)
        {
            pszStrToCopy = (PWSTR) SzLoadIds(IDS_CONFOLD_WIZARD_DISPLAY_NAME);
        }
        else if (WIZARD_HNW == pcfpLatestVersion->wizWizard)
        {
            pszStrToCopy = (PWSTR) SzLoadIds(IDS_CONFOLD_HOMENET_WIZARD_DISPLAY_NAME);
        }
        else
        {
            hr = g_ccl.HrGetCachedPidlCopyFromPidl(pcfpLatestVersion, pcfpLatestVersionCached);
            if (S_OK == hr)
            {
                pszStrToCopy = pcfpLatestVersionCached->PszGetNamePointer();
            }
            else
            {
                pszStrToCopy = pcfpLatestVersion->PszGetNamePointer();
                hr = S_OK;
            }
        }

        Assert(pszStrToCopy);

        // Allocate a new POLESTR block, which the shell can then free,
        // and copy the displayable portion to it.
        //
        // Note that &lpName->pOleStr is likely misaligned.
        //

        LPWSTR pOleStr;

        pOleStr = lpName->pOleStr;

        hr = HrDupeShellString(pszStrToCopy, &pOleStr );

        lpName->pOleStr = pOleStr;
    }
    else if (PIDL_TYPE_98 == cfpt)
    {
        // Raid#214057, handle win98 pidl for shortcuts
        // Return the offset to the string because we store the display
        // name in the opaque structure.

        lpName->uType = STRRET_OFFSET;
        lpName->uOffset = _IOffset(CONFOLDPIDL98, szaName);
    }
    else
    {
        // not a valid connections pidl (neither Win2K nor Win98).
        //
        hr = E_INVALIDARG;
    }

    TraceHr(ttidError, FAL, hr, FALSE, "CConnectionFolder::GetDisplayNameOf");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::SetNameOf
//
//  Purpose:    Changes the name of a file object or subfolder, changing its
//              item identifier in the process.
//
//  Arguments:
//      hwndOwner [in]      Handle of owner window
//      pidl      [in]      Pointer to an ITEMIDLIST structure
//      lpszName  [in]      Pointer to string specifying new display name
//      uFlags    [in]      Type of name specified in lpszName
//      ppidlOut  [out]     Pointer to new ITEMIDLIST
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise.
//
//  Author:     jeffspr   18 Oct 1997
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::SetNameOf(
    HWND            hwndOwner,
    LPCITEMIDLIST   pidlShell,
    LPCOLESTR       lpszName,
        DWORD           uFlags,
    LPITEMIDLIST *  ppidlOut)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT             hr          = NOERROR;
    /*
    PWSTR              pszWarning  = NULL;
    INetConnection *    pNetCon     = NULL;
    LPITEMIDLIST        pidlNew     = NULL;
    BOOL                fRefresh    = FALSE;
    BOOL                fActivating = FALSE;
    PCONFOLDENTRY      pccfe       = NULL;
    */
    PCONFOLDPIDL        pcfp;

    Assert(hwndOwner);
    Assert(pidlShell);
    Assert(lpszName);

    if (!pidlShell && !lpszName)
    {
        hr = E_INVALIDARG;
    }
    else
    {
        // check lpszName for validity

        if (!FIsValidConnectionName(lpszName))
        {
            (void) NcMsgBox(
                _Module.GetResourceInstance(),
                hwndOwner,
                IDS_CONFOLD_RENAME_FAIL_CAPTION,
                IDS_CONFOLD_RENAME_INVALID,
                MB_OK | MB_ICONEXCLAMATION);
            hr = HRESULT_FROM_WIN32(ERROR_INVALID_NAME);
        }

        if (SUCCEEDED(hr))
        {
            // Get what's current from the cache so rename works properly
            //
            PCONFOLDPIDL pcfpShell;
            hr = pcfpShell.InitializeFromItemIDList(pidlShell);
            if (SUCCEEDED(hr))
            {
                hr = g_ccl.HrGetCachedPidlCopyFromPidl(pcfpShell, pcfp);
                if (SUCCEEDED(hr))
                {
                    PCONFOLDPIDL pidlOut;
                    hr = HrRenameConnectionInternal(pcfp, m_pidlFolderRoot, lpszName, TRUE, hwndOwner, pidlOut);
                    if ( (ppidlOut) && (SUCCEEDED(hr)) )
                    {
                        *ppidlOut = pidlOut.TearOffItemIdList();
                    }
                }
            }
        }
    }

    if (FAILED(hr) && (ppidlOut))
    {
        *ppidlOut = NULL;
    }

    TraceHr(ttidError, FAL, hr, FALSE, "CConnectionFolder::SetNameOf");
    return hr;
}

STDMETHODIMP CConnectionFolder::MessageSFVCB(
        UINT uMsg,
        WPARAM wParam,
        LPARAM lParam)
{
    TraceFileFunc(ttidShellFolder);
    
    HRESULT hr = RealMessage(uMsg, wParam, lParam);
    if (FAILED(hr))
    {
        switch (uMsg)
        {
        case DVM_INVOKECOMMAND:
            if ((CMIDM_RENAME == wParam) && m_hwndMain && m_pShellView)
            {
                PCONFOLDPIDLVEC apidlSelected;
                PCONFOLDPIDLVEC apidlCache;
                hr = HrShellView_GetSelectedObjects(m_hwndMain, apidlSelected);
                if (SUCCEEDED(hr))
                {
                    // If there are objects, try to get the cached versions
                    if (!apidlSelected.empty())
                    {   
                        hr = HrCloneRgIDL(apidlSelected, TRUE, TRUE, apidlCache);
                    }
                }

                if (SUCCEEDED(hr))
                {
                    Assert(apidlCache.size() == 1);
                    if (apidlCache.size() == 1)
                    {
                        hr = m_pShellView->SelectItem(apidlCache[0].GetItemIdList(), SVSI_EDIT);
                    }
                    else
                    {
                        hr = E_INVALIDARG;
                    }
                }
            }
            break;

        case SFVM_HWNDMAIN:
            // _hwndMain = (HWND)lParam;
            hr = S_OK;
            break;
        }
    }
    return hr;
}

/*
//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::GetOverlayIndex
//
//  Purpose:    Adds icon overlays to connections that need them
//
//  Arguments:
//      pidlItem [in]     Pidl to item in question
//      pIndex [out]      Address of overlay index into system image list
//        
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise.
//
//  Author:     kenwic   10 May 2000 created, support for sharing overlay
//
//  Notes:
//

STDMETHODIMP CConnectionFolder::GetOverlayIndex(
    LPCITEMIDLIST pidlItem,
    int* pIndex)
{
    TraceFileFunc(ttidShellFolder);

    HRESULT hResult = E_FAIL;
    *pIndex = -1;

    // check to see if connection is sharing, and if so add sharing hand overlay
    // i can't call HrNetConFromPidl, because it asserts if passed the wizard icon
    
    PCONFOLDPIDL pcfpItem;
    pcfpItem.InitializeFromItemIDList(pidlItem);

    CONFOLDENTRY pConnectionFolderEntry;
    hResult = pcfpItem.ConvertToConFoldEntry(pConnectionFolderEntry);
    if(SUCCEEDED(hResult))
    {
        if(FALSE == pConnectionFolderEntry.GetWizard()) // sharing the wizard is not yet supported
        {
            if(NCCF_SHARED & pConnectionFolderEntry.GetCharacteristics())
            {
                *pIndex = SHGetIconOverlayIndex(NULL, IDO_SHGIOI_SHARE);
                hResult = S_OK;
            }
            else
            {
                hResult = E_FAIL; // the docs for IShellIconOverlay are wrong, we must return failure to deny the icon
            }
        }
        else
        {
            hResult = E_FAIL;
        }
    }
    
    TraceHr(ttidShellFolder, FAL, hResult, TRUE, "CConnectionFolder::GetOverlayIndex");
    return hResult;
}

//+---------------------------------------------------------------------------
//
//  Member:     CConnectionFolder::GetOverlayIconIndex
//
//  Purpose:    Adds icon overlays to connections that need them
//
//  Arguments:
//      pidlItem [in]     Pidl to item in question
//      pIconIndex [out]      Address of index into system image list
//        
//
//  Returns:    Returns NOERROR if successful or an OLE-defined error
//              value otherwise.
//
//  Author:     kenwic   10 May 2000 created
//
//  Notes:
//
STDMETHODIMP CConnectionFolder::GetOverlayIconIndex(
    LPCITEMIDLIST pidlItem,
    int* pIconIndex)
{
    TraceFileFunc(ttidShellFolder);

    *pIconIndex = -1;

    HRESULT hResult = GetOverlayIndex(pidlItem, pIconIndex);
    if(SUCCEEDED(hResult))
    {
        *pIconIndex = INDEXTOOVERLAYMASK(*pIconIndex);
    }

    TraceHr(ttidShellFolder, FAL, hResult, TRUE, "CConnectionFolder::GetOverlayIconIndex");
    return hResult;
}*/