#include "pch.h"
#pragma hdrstop

#include "foldinc.h"    // Standard shell\folder includes
#include "conprops.h"
#include "ncnetcon.h"
#include "foldres.h"

class CConnectionPropPages
{
public:
    CConnectionPropPages();
    ~CConnectionPropPages();
    ULONG CntPages() {return m_culPages;}
    HPROPSHEETPAGE * PHPages() {return m_rghPages;}
    static BOOL FAddPropSheet(HPROPSHEETPAGE hPage, LPARAM lParam);

private:
    ULONG               m_culPages;
    ULONG               m_ulPageBufferLen;
    HPROPSHEETPAGE *    m_rghPages;
};

CConnectionPropPages::CConnectionPropPages()
{
    m_culPages = 0;
    m_ulPageBufferLen = 0;
    m_rghPages = NULL;
}

CConnectionPropPages::~CConnectionPropPages()
{
    delete [] (BYTE *)(m_rghPages);
}

//
// Function:    CConnectionPropPages::FAddPropSheet
//
// Purpose:     Callback function for the AddPages API used to accept
//              connection property pages handed back from a provider.
//
// Parameters:  hPage  [IN] - The page to add
//              lParam [IN] - 'this' casted to an LPARAM
//
// Returns:     BOOL, TRUE if the page was successfully added.
//
BOOL
CConnectionPropPages::FAddPropSheet(HPROPSHEETPAGE hPage, LPARAM lParam)
{
    CConnectionPropPages * pCPP = NULL;

    // Validate the input parameters
    //
    if ((0L == lParam) || (NULL == hPage))
    {
        Assert(lParam);
        Assert(hPage);

        TraceHr(ttidShellFolder, FAL, E_INVALIDARG, FALSE, "CConnectionPropPages::FAddPropSheet");
        return FALSE;
    }

    pCPP = reinterpret_cast<CConnectionPropPages*>(lParam);

    // Grow the buffer if necessary
    //
    if (pCPP->m_culPages == pCPP->m_ulPageBufferLen)
    {
        HPROPSHEETPAGE* rghPages = NULL;

        rghPages = (HPROPSHEETPAGE*)(new BYTE[sizeof(HPROPSHEETPAGE) *
                                   (pCPP->m_ulPageBufferLen + 10)]);

        if (NULL == rghPages)
        {
            TraceHr(ttidShellFolder, FAL, E_OUTOFMEMORY, FALSE, "CConnectionPropPages::FAddPropSheet");
            return FALSE;
        }

        // Copy the existing pages to the new buffer
        //
        memcpy(rghPages, pCPP->m_rghPages,
               sizeof(HPROPSHEETPAGE) * pCPP->m_ulPageBufferLen);
        delete [] (BYTE *)(pCPP->m_rghPages);

        pCPP->m_rghPages = rghPages;
        pCPP->m_ulPageBufferLen += 10;
    }

    // Retain the new page
    //
    pCPP->m_rghPages[pCPP->m_culPages++] = hPage;

    return TRUE;
}

//+---------------------------------------------------------------------------
//
//  Function:   HrUIInterfaceFromNetCon
//
//  Purpose:    Get the INetConnectionPropertyUI interface from an
//              INetConnection pointer.
//
//  Arguments:
//      pconn [in]      Valid INetConnection *
//      riid  [in]      IID of desired interface
//      ppv   [out]     Returned pointer to the interface
//
//  Returns:
//
//  Author:     jeffspr   12 Nov 1997
//
//  Notes:
//
HRESULT HrUIInterfaceFromNetCon(
    INetConnection *            pconn,
    REFIID                      riid,
    LPVOID *                    ppv)
{
    HRESULT hr      = S_OK;
    CLSID   clsid;

    Assert(pconn);
    Assert(ppv);

    // Validate the parameters.
    //
    if ((NULL == pconn) || (NULL == ppv))
    {
        hr = E_INVALIDARG;
        goto Error;
    }

    // Initailize the output parameter.
    //
    *ppv = NULL;

    // Get the CLSID of the object which can provide the particular interface.
    //
    hr = pconn->GetUiObjectClassId(&clsid);
    if (FAILED(hr))
    {
        goto Error;
    }

    // Create this object asking for the specified interface.
    //
    hr = CoCreateInstance(clsid, NULL,
            CLSCTX_INPROC_SERVER | CLSCTX_NO_CODE_DOWNLOAD, riid, ppv);

Error:

    TraceHr(ttidError, FAL, hr, (E_NOINTERFACE == hr),
        "HrUIInterfaceFromNetCon");
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrGetPropertiesCaption
//
//  Purpose:    Generate the caption for a property page
//
//  Arguments:
//      pconn       [in]  Connection pointer passed in from the shell
//      ppszCaption [out] Resultant property page caption if successful
//
//  Returns:
//
//  Notes:
//
HRESULT HrGetPropertiesCaption(INetConnection * pconn, PWSTR * ppszCaption)
{
    HRESULT hr;

    Assert(pconn);
    Assert(ppszCaption);

    // Try to get the connection name
    //
    NETCON_PROPERTIES* pProps;
    hr = pconn->GetProperties(&pProps);
    if (SUCCEEDED(hr))
    {
        Assert (pProps->pszwName);

        FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                      FORMAT_MESSAGE_FROM_STRING |
                      FORMAT_MESSAGE_ARGUMENT_ARRAY,
                      SzLoadIds(IDS_CONPROP_CAPTION),
                      0, 0, (PWSTR)ppszCaption, 0,
                      (va_list *)&pProps->pszwName);

        FreeNetconProperties(pProps);
    }

    TraceHr(ttidError, FAL, hr, FALSE,"HrGetPropertiesCaption");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   ActivatePropertyDialog
//
//  Purpose:    Try to locate a property dialog associated with pconn
//              then bring it to the foreground.
//
//  Arguments:
//      pconn       [in]  Connection pointer passed in from the shell
//
//  Returns:
//
//  Notes:
//
VOID ActivatePropertyDialog(INetConnection * pconn)
{
    PWSTR pszCaption = NULL;

    if (SUCCEEDED(HrGetPropertiesCaption(pconn, &pszCaption)))
    {
        Assert(pszCaption);

        // Find the dialog with this caption
        //
        HWND hwnd = FindWindow(NULL, pszCaption);
        if (IsWindow(hwnd))
        {
            SetForegroundWindow(hwnd);
        }

        LocalFree (pszCaption);
    }
}

//+---------------------------------------------------------------------------
//
//  Function:   HrSetPropertiesTaskbarIcon
//
//  Purpose:    Setup the dialog property sheet's taskbar icon.
//
//  Arguments:
//      hwndDlg  [in]  Dialog handle
//      uMsg     [in]  Message value
//      lparam   [in]  Long parameter
//
//  Returns:    0
//
//  Notes:      A standard Win32 commctrl PropSheetProc always return 0.  
//              See MSDN documentation.
//
int CALLBACK HrSetPropertiesTaskbarIcon(
    IN HWND   hwndDlg,
    IN UINT   uMsg,
    IN LPARAM lparam)

{
    switch (uMsg)
    {
        case PSCB_INITIALIZED:

            // Set the dialog window's icon

            // NTRAID#NTBUG9-366302-2001/04/11-roelfc Alt-tab icon
            // This requires a re-architecture in order to be able to retrieve
            // the appropiate icon for the property page through the 
            // IID_INetConnectionPropertyUi2 interface.

            // In the mean time, we query the small icon through the only link we have,
            // the dialog handle, and assign it as the big icon as well. Stretching the 
            // small icon is better than nothing at all...
            HICON  hIcon;

            hIcon = (HICON)SendMessage(hwndDlg, 
                                       WM_GETICON,
                                       ICON_SMALL,
                                       0);
            Assert(hIcon);

            if (hIcon)
            {
                SendMessage(hwndDlg,
                            WM_SETICON,
                            ICON_BIG,
                            (LPARAM)hIcon);
            }
            break;

        default:
            break;

    }

    return 0;
}

//+---------------------------------------------------------------------------
//
//  Function:   HrRaiseConnectionPropertiesInternal
//
//  Purpose:    Bring up the propsheet page UI for the passed in connection
//
//  Arguments:
//      hwnd  [in]  Owner hwnd
//      pconn [in]  Connection pointer passed in from the shell
//
//  Returns:
//
//  Author:     jeffspr   12 Nov 1997
//
//  Notes:
//
HRESULT HrRaiseConnectionPropertiesInternal(HWND hwnd, UINT nStartPage, INetConnection * pconn)
{
    HRESULT                     hr          = NOERROR;
    INetConnectionPropertyUi *  pPUI        = NULL;
    PWSTR                       pszCaption  = NULL;

    Assert(pconn);
    hr = HrUIInterfaceFromNetCon(pconn, IID_INetConnectionPropertyUi,
            reinterpret_cast<void**>(&pPUI));

    if (E_NOINTERFACE == hr)
    {
        // What we want to check for here, is an object that when QI'd doesn't support IID_INetConnectionPropertyUi
        // but support IID_INetConnectionPropertyUi2.
        //
        // A reinterpret style-downcast directly from the QI would have been ok since INetConnectionPropertyUi2 inherit from 
        // INetConnectionPropertyUi. Hence an object can't multi-inherit from both, so we'll never have both vtable entries.
        // We could simply get the INetConnectionPropertyUi2 vtable entry and treat it like an INetConnectionPropertyUi.
        //
        // However, I'm doing the dynamic cast anyway since a cast-to-wrong-vtable is one of the most difficult
        // bugs to spot.
        INetConnectionPropertyUi2 *pPUI2 = NULL;
        hr = HrUIInterfaceFromNetCon(pconn, IID_INetConnectionPropertyUi2,
                reinterpret_cast<void**>(&pPUI2));

        if (SUCCEEDED(hr))
        {
            pPUI = dynamic_cast<INetConnectionPropertyUi *>(pPUI2);
        }
    }

    if (SUCCEEDED(hr))
    {
        INetConnectionUiLock * pUiLock = NULL;

        // Try to get the connection name
        //
        (VOID)HrGetPropertiesCaption(pconn, &pszCaption);

        Assert(pPUI);
        hr = pPUI->QueryInterface(IID_INetConnectionUiLock, (LPVOID *)&pUiLock);
        if (SUCCEEDED(hr))
        {
            // If the interface exists, we have work to do.
            PWSTR pszwMsg = NULL;
            hr = pUiLock->QueryLock(&pszwMsg);
            ReleaseObj(pUiLock);

            if (S_FALSE == hr)
            {
                // Format the error text
                //
                PWSTR  pszText = NULL;
                PCWSTR  pcszwTemp = pszwMsg;
                if (NULL == pcszwTemp)
                {
                    // Load <Unknown Application>
                    //
                    pcszwTemp = SzLoadIds(IDS_CONPROP_GENERIC_COMP);
                }

                Assert(pcszwTemp);
                FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
                              FORMAT_MESSAGE_FROM_STRING |
                              FORMAT_MESSAGE_ARGUMENT_ARRAY,
                              SzLoadIds(IDS_CONPROP_NO_WRITE_LOCK),
                              0, 0, (PWSTR)&pszText, 0,
                              (va_list *)&pcszwTemp);

                if (pszwMsg)
                {
                    CoTaskMemFree(pszwMsg);
                }

                // No UI, couldn't acquire the lock
                //
                if (pszText)
                {
                    MessageBox(hwnd, pszText,
                               (pszCaption ? pszCaption : c_szEmpty),
                               MB_OK | MB_ICONERROR);

                    LocalFree(pszText);
                }

                goto Error;
            }
            else if (FAILED(hr))
            {
                goto Error;
            }
        }

        BOOL fShouldDestroyIcon = FALSE;

        hr = pPUI->SetConnection(pconn);
        if (SUCCEEDED(hr))
        {
            CComPtr<INetConnectionPropertyUi2> pUI2;

            HICON hIcon         = NULL;
            DWORD dwDisplayIcon = 0;
            hr = pPUI->QueryInterface(IID_INetConnectionPropertyUi2, reinterpret_cast<LPVOID *>(&pUI2) );
            if (SUCCEEDED(hr))
            {
                Assert(GetSystemMetrics(SM_CXSMICON) == GetSystemMetrics(SM_CYSMICON));
                
                hr = pUI2->GetIcon(GetSystemMetrics(SM_CXSMICON), &hIcon);
                if (SUCCEEDED(hr))
                {
                    fShouldDestroyIcon = TRUE;
                    dwDisplayIcon = PSH_USEHICON;
                }
                else
                {
                    hIcon = NULL;
                }
            }
            else
            {
                TraceTag(ttidError, "QI for INetConnectionPropertyUi2 failed using Default Icon");
                hIcon = LoadIcon(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDI_CONNECTIONS_FOLDER_LARGE2));
                if (hIcon)
                {
                    dwDisplayIcon = PSH_USEHICON;
                }
            }
            Assert(hIcon);
                
            CConnectionPropPages    CPP;

            // Get the pages from the provider
            hr = pPUI->AddPages(hwnd,
                                CConnectionPropPages::FAddPropSheet,
                                reinterpret_cast<LPARAM>(&CPP));

            // If any pages were returned, display them
            if (SUCCEEDED(hr) && CPP.CntPages())
            {

                PROPSHEETHEADER     psh;
                ZeroMemory (&psh, sizeof(psh));
                psh.dwSize      = sizeof( PROPSHEETHEADER );
                psh.dwFlags     = PSH_NOAPPLYNOW | PSH_USECALLBACK | dwDisplayIcon;
                psh.hwndParent  = hwnd;
                psh.hInstance   = _Module.GetResourceInstance();
                psh.pszCaption  = pszCaption;
                psh.nPages      = CPP.CntPages();
                psh.phpage      = CPP.PHPages();
                psh.hIcon       = hIcon;
                psh.nStartPage  = nStartPage;
                psh.pfnCallback = HrSetPropertiesTaskbarIcon;

                // nRet used for debugging only
                //
                int nRet = PropertySheet(&psh);

                if (fShouldDestroyIcon)
                {
                    DestroyIcon(hIcon);
                }
            }
        }

Error:
        ReleaseObj(pPUI);
    }

    // Cleanup
    //
    if (pszCaption)
    {
        LocalFree (pszCaption);
    }

    TraceHr(ttidShellFolder, FAL, hr, FALSE, "HrRaiseConnectionProperties");
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   HrHandleDisconnectHResult
//
//  Purpose:    Put up the message box assocated with an HRESULT from
//              a diconnect operation if the HRESULT represents a failure.
//              Also translate any success codes back to S_OK as needed.
//
//  Arguments:
//      hr    [in] The HRESULT returned from a connection disconnect method.
//      pconn [in] INetConnection* for checking if this is a LAN or RAS connection.
//
//  Returns:    The translated HRESULT if needed
//
//  Author:     shaunco   3 Jun 1999
//
HRESULT HrHandleDisconnectHResult(HRESULT hr, INetConnection * pconn)
{
    if (FAILED(hr))
    {
       	NETCON_PROPERTIES* pProps = NULL;
        UINT nFailureCaptionID;
        UINT nFailureMessageID;

        TraceHr(ttidShellFolder, FAL, hr, FALSE, "pNetCon->Disconnect() failed");

        // Assume that is is a RAS/DIALUP connection unless we find otherwise
        nFailureCaptionID = IDS_CONFOLD_DISCONNECT_FAILURE_CAPTION;
        nFailureMessageID = IDS_CONFOLD_DISCONNECT_FAILURE;

    	hr = pconn->GetProperties(&pProps);
    	if (SUCCEEDED(hr))
    	{
    	    if (NCM_LAN == pProps->MediaType)
            {
                nFailureCaptionID = IDS_CONFOLD_DISABLE_FAILURE_CAPTION;
                nFailureMessageID = IDS_CONFOLD_DISABLE_FAILURE;
            }

    	    FreeNetconProperties(pProps);
    	}

        // Ignore the return from this, since we only allow OK
        //
        (void) NcMsgBox(
            _Module.GetResourceInstance(),
            NULL,
            nFailureCaptionID,
            nFailureMessageID,
            MB_OK | MB_ICONEXCLAMATION);

    }
    else
    {
        // If we get the object_nlv return, it means that the connection
        // is deleted on disconnect and we shouldn't perform an update of
        // that connection. We can normalize this for now, as we'll let the
        // notifysink take care of the delete update.
        //
        if (S_OBJECT_NO_LONGER_VALID == hr)
        {
            hr = S_OK;
        }
    }
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   HrConnectOrDisconnectNetConObject
//
//  Purpose:    Bring up the connection UI and make the connection for the
//              passed in connection.
//
//  Arguments:
//      hwnd  [in] Owner hwnd
//      pconn [in] Connection pointer passed in from the shell
//      Flag  [in] CD_CONNECT or CD_DISCONNECT.
//
//  Returns:
//
//  Author:     jeffspr   12 Nov 1997
//
//  Notes:
//
HRESULT HrConnectOrDisconnectNetConObject(HWND hwnd, INetConnection * pconn,
                                          CDFLAG Flag)
{
    HRESULT                     hr          = NOERROR;
    INetConnectionConnectUi *   pCUI        = NULL;
	
    Assert(pconn);

    // Get the INetConnectionConnectUi interface from the connection
    //
    hr = HrUIInterfaceFromNetCon(pconn, IID_INetConnectionConnectUi,
            reinterpret_cast<void**>(&pCUI));
    if (SUCCEEDED(hr))
    {
        Assert(pCUI);

        // Set the connection on the UI object
        //
        hr = pCUI->SetConnection(pconn);
        if (SUCCEEDED(hr))
        {
            if (CD_CONNECT == Flag)
            {
                // Connect, dangit!
                //
                hr = pCUI->Connect(hwnd, NCUC_DEFAULT);
                if (SUCCEEDED(hr))
                {
                    // heh heh, uhhhh, heh heh. Cool.
                }
                else if (HRESULT_FROM_WIN32(ERROR_NOT_CONNECTED) == hr)
                {
                    // Ignore the return from this, since we only allow OK
                    //
                    (void) NcMsgBox(
                        _Module.GetResourceInstance(),
                        hwnd,
                        IDS_CONFOLD_CONNECT_FAILURE_CAPTION,
                        IDS_CONFOLD_CONNECT_FAILURE,
                        MB_OK | MB_ICONEXCLAMATION);
                }
            }
            else
            {
                // Disconnect the connection object
                //
                hr = pCUI->Disconnect(hwnd, NCUC_DEFAULT);
                hr = HrHandleDisconnectHResult(hr, pconn);
            }
        }

        ReleaseObj(pCUI);
    }
    else if ((E_NOINTERFACE == hr) && (CD_DISCONNECT == Flag))
    {
        // Incoming connection objects do not have a UI interface
        // so we disconect them on the object itself.
        //
        hr = pconn->Disconnect ();
        hr = HrHandleDisconnectHResult(hr, pconn);
    }

    AssertSz(E_NOINTERFACE != hr,
             "Should not have been able to attempt connection on object that doesn't support this interface");

    TraceHr(ttidShellFolder, FAL, hr, (E_NOINTERFACE == hr),
        "HrConnectOrDisconnectNetConObject");
    return hr;
}