/*++

   Copyright    (c)    1994-1998    Microsoft Corporation

   Module  Name :

        dataobj.cpp

   Abstract:

        Snapin data object

   Author:

        Ronald Meijer (ronaldm)

   Project:

        Internet Services Manager

   Revision History:

--*/

//
// Include Files
//
#include "stdafx.h"
#include "inetmgr.h"
#include "cinetmgr.h"
#include "dataobj.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

//
// Snap-in NodeType in both GUID format and string format
// Note - Typically there is a node type for each different object, sample
// only uses one node type.
//
unsigned int CDataObject::m_cfNodeType       = RegisterClipboardFormat(CCF_NODETYPE);
unsigned int CDataObject::m_cfNodeTypeString = RegisterClipboardFormat(CCF_SZNODETYPE);
unsigned int CDataObject::m_cfDisplayName    = RegisterClipboardFormat(CCF_DISPLAY_NAME);
unsigned int CDataObject::m_cfCoClass        = RegisterClipboardFormat(CCF_SNAPIN_CLASSID);

/*
//
// Multi-select
//
unsigned int CDataObject::m_cfpMultiSelDataObj = RegisterClipboardFormat(CCF_MMC_MULTISELECT_DATAOBJECT);
unsigned int CDataObject::m_cfMultiObjTypes    = RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT);
unsigned int CDataObject::m_cfMultiSelDataObjs = RegisterClipboardFormat(CCF_MULTI_SELECT_SNAPINS);
*/

//
// Internal
//
unsigned int CDataObject::m_cfInternal       = RegisterClipboardFormat(ISM_SNAPIN_INTERNAL);

//
// Published Information
//
unsigned int CDataObject::m_cfISMMachineName = RegisterClipboardFormat(ISM_SNAPIN_MACHINE_NAME);
unsigned int CDataObject::m_cfMyComputMachineName = RegisterClipboardFormat(MYCOMPUT_MACHINE_NAME);
unsigned int CDataObject::m_cfISMService     = RegisterClipboardFormat(ISM_SNAPIN_SERVICE);
unsigned int CDataObject::m_cfISMInstance    = RegisterClipboardFormat(ISM_SNAPIN_INSTANCE);
unsigned int CDataObject::m_cfISMParentPath  = RegisterClipboardFormat(ISM_SNAPIN_PARENT_PATH);
unsigned int CDataObject::m_cfISMNode        = RegisterClipboardFormat(ISM_SNAPIN_NODE);
unsigned int CDataObject::m_cfISMMetaPath    = RegisterClipboardFormat(ISM_SNAPIN_META_PATH);




STDMETHODIMP 
CDataObject::GetDataHere(
    IN LPFORMATETC lpFormatetc,
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Write requested information type to the given medium

Arguments:

    LPFORMATETC lpFormatetc     : Format etc
    LPSTGMEDIUM lpMedium        : Medium to write to.

Return Value:

    HRESULT

--*/
{
    HRESULT hr = DV_E_CLIPFORMAT;

    //
    // Based on the CLIPFORMAT write data to the stream
    //
    const CLIPFORMAT cf = lpFormatetc->cfFormat;

    if (cf == m_cfNodeType)
    {
        hr = CreateNodeTypeData(lpMedium);
    }
    else if (cf == m_cfNodeTypeString)
    {
        hr = CreateNodeTypeStringData(lpMedium);
    }
    else if (cf == m_cfCoClass)
    {
        hr = CreateCoClassID(lpMedium);
    }
    else if (cf == m_cfDisplayName)
    {
        hr = CreateDisplayName(lpMedium);
    }
    else if (cf == m_cfInternal)
    {
        hr = CreateInternal(lpMedium);
    }
    else if (cf == m_cfISMMachineName)
    {
        hr = CreateMachineName(lpMedium);
    }

/*
    multi-select pulled

    else if (cf == m_cfpMultiSelDataObj)
    {
        TRACEEOLID("CCF_MMC_MULTISELECT_DATAOBJECT");
    }
    else if (cf == m_cfMultiObjTypes)
    {
        TRACEEOLID("CCF_OBJECT_TYPES_IN_MULTI_SELECT");
    }
    else if (cf == m_cfMultiSelDataObjs)
    {
         TRACEEOLID("CCF_MULTI_SELECT_SNAPINS");
    }
*/

    else if (cf == m_cfISMService)
    {
        hr = CreateMetaField(lpMedium, META_SERVICE);
    }
    else if (cf == m_cfISMInstance)
    {
        hr = CreateMetaField(lpMedium, META_INSTANCE);
    }
    else if (cf == m_cfISMParentPath)
    {
        hr = CreateMetaField(lpMedium, META_PARENT);
    }
    else if (cf == m_cfISMNode)
    {
        hr = CreateMetaField(lpMedium, META_NODE);
    }
    else if (cf == m_cfISMMetaPath)
    {
        hr = CreateMetaField(lpMedium, META_WHOLE);
    }
    else
    {
        TRACEEOLID("Unrecognized format");
        hr = DV_E_CLIPFORMAT;
    }

    return hr;
}



STDMETHODIMP
CDataObject::GetData(
    IN LPFORMATETC lpFormatetcIn,
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Get data from the data object

Arguments:

    LPFORMATETC lpFormatetcIn   : Formatetc Input
    LPSTGMEDIUM lpMedium        : Pointer to medium

Return Value:

    HRESULT

--*/
{
    //
    // Not implemented
    //
    return E_NOTIMPL;
}



STDMETHODIMP
CDataObject::EnumFormatEtc(
    IN DWORD dwDirection,
    IN LPENUMFORMATETC * ppEnumFormatEtc
    )
/*++

Routine Description:

    Enumerate format etc information

Arguments:

    DWORD dwDirection                   : Direction
    LPENUMFORMATETC * ppEnumFormatEtc   : Format etc array


Return Value:

    HRESULT

--*/
{
    //
    // Not implemented
    //
    return E_NOTIMPL;
}



//
// CDataObject creation members
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<



HRESULT
CDataObject::Create(
    IN const void * pBuffer,
    IN int len,
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Create information on the given medium

Arguments:

    const void * pBuffer        : Data buffer
    int len                     : Size of data
    LPSTGMEDIUM lpMedium        : Medium to write to.

Return Value:

    HRESULT

--*/
{
    HRESULT hr = DV_E_TYMED;

    //
    // Do some simple validation
    //
    if (pBuffer == NULL || lpMedium == NULL)
    {
        return E_POINTER;
    }

    //
    // Make sure the type medium is HGLOBAL
    //
    if (lpMedium->tymed == TYMED_HGLOBAL)
    {
        //
        // Create the stream on the hGlobal passed in
        //
        LPSTREAM lpStream;
        hr = CreateStreamOnHGlobal(lpMedium->hGlobal, FALSE, &lpStream);

        if (SUCCEEDED(hr))
        {
            //
            // Write to the stream the number of bytes
            //
            unsigned long written;
            hr = lpStream->Write(pBuffer, len, &written);

            //
            // Because we called CreateStreamOnHGlobal with 'FALSE',
            // only the stream is released here.
            // Note - the caller (i.e. snap-in, object) will free
            // the HGLOBAL at the correct time.  This is according to
            // the IDataObject specification.
            //
            lpStream->Release();
        }
    }

    return hr;
}



HRESULT
CDataObject::CreateNodeTypeData(
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Create node type data on medium

Arguments:

    LPSTGMEDIUM lpMedium        : Medium to write to.

Return Value:

    HRESULT

--*/
{
    //
    // Create the node type object in GUID format
    //
    const GUID * pcObjectType = NULL;

    if (m_internal.m_cookie == NULL)
    {
        //
        // Blank CIISNode, must be the static root
        //
        pcObjectType = &cInternetRootNode;
    }
    else
    {
        //
        // Ask the object what kind of object it is.
        //
        CIISObject * pObject = (CIISObject *)m_internal.m_cookie;
        pcObjectType = pObject->GetGUIDPtr();
    }

    return Create((const void *)pcObjectType, sizeof(GUID), lpMedium);
}



HRESULT 
CDataObject::CreateNodeTypeStringData(
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Create node type in string format

Arguments:

    LPSTGMEDIUM lpMedium        : Medium to write to.

Return Value:

    HRESULT

--*/
{
    //
    // Create the node type object in GUID string format
    //
    const wchar_t * cszObjectType = NULL;
    CString str;

    if (m_internal.m_cookie == NULL)
    {
        //
        // This must be the static root node
        //
        cszObjectType = GUIDToCString(cInternetRootNode, str);
    }
    else
    {
        //
        // Ask the CIISObject what type it is
        //
        CIISObject * pObject = (CIISObject *)m_internal.m_cookie;
        cszObjectType = GUIDToCString(pObject->QueryGUID(), str);
    }

    return Create(
        cszObjectType,
        ((wcslen(cszObjectType) + 1) * sizeof(wchar_t)),
        lpMedium
        );
}



HRESULT
CDataObject::CreateDisplayName(
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    CDataObject implementations

Arguments:

Return Value:

    HRESULT

--*/
{
    //
    // This is the display name used in the scope pane and snap-in manager
    //
    // Load the name from resource
    // Note - if this is not provided, the console will used the snap-in name
    //

    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    CString szDispName;
    szDispName.LoadString(IDS_NODENAME);

    return Create(
        szDispName,
        ((szDispName.GetLength() + 1) * sizeof(wchar_t)),
        lpMedium
        );
}



HRESULT
CDataObject::CreateInternal(
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    CDataObject implementations

Arguments:

Return Value:

    HRESULT

--*/
{
    return Create(&m_internal, sizeof(INTERNAL), lpMedium);
}



HRESULT
CDataObject::CreateMachineName(
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Create the machine name

Arguments:

    LPSTGMEDIUM lpMedium  : Address of STGMEDIUM object

Return Value:

    HRESULT

--*/
{
    wchar_t pzName[MAX_PATH + 1] = {0};
    DWORD len; // = MAX_PATH + 1;

    CIISObject * pObject = (CIISObject *)m_internal.m_cookie;
    if (pObject == NULL)
    {
        ASSERT(FALSE);
        return E_FAIL;
    }

    lstrcpyn(pzName, pObject->GetMachineName(), MAX_PATH);
	len = lstrlen(pzName);

    //
    // Add 1 for the NULL and calculate the bytes for the stream
    //
    return Create(pzName, ((len + 1) * sizeof(wchar_t)), lpMedium);
}



HRESULT
CDataObject::CreateMetaField(
    IN LPSTGMEDIUM lpMedium,
    IN META_FIELD fld
    )
/*++

Routine Description:

    Create a field from the metabase path, indicated by fld, or the entire
    path.

Arguments:

    LPSTGMEDIUM lpMedium  : Address of STGMEDIUM object
    META_FIELD fld        : Type of metabase information

Return Value:

    HRESULT

--*/
{
    CIISObject * pObject = (CIISObject *)m_internal.m_cookie;

    if (pObject == NULL)
    {
        //
        // Static root node has no metabase path
        //
        ASSERT(FALSE);
        return E_FAIL;
    }

    //
    // Generate complete metabase path for this node
    //
    CString strField;
    CString strMetaPath;
    pObject->BuildFullPath(strMetaPath, TRUE);

    if (fld == META_WHOLE)
    {
        //
        // Whole metabase path requested
        //
        strField = strMetaPath;
    }
    else
    {
        //
        // A portion of the metabase is requested.  Return the requested
        // portion
        //
        LPCTSTR lpMetaPath = (LPCTSTR)strMetaPath;
        LPCTSTR lpEndPath = lpMetaPath + strMetaPath.GetLength() + 1;
        LPCTSTR lpSvc = NULL;
        LPCTSTR lpInstance = NULL;
        LPCTSTR lpParent = NULL;
        LPCTSTR lpNode = NULL;

        //
        // Break up the metabase path in portions
        //
        if (lpSvc = _tcschr(lpMetaPath, _T('/')))
        {
            ++lpSvc;

            if (lpInstance = _tcschr(lpSvc, _T('/')))
            {
                ++lpInstance;

                if (lpParent = _tcschr(lpInstance, _T('/')))
                {
                    ++lpParent;
                    lpNode = _tcsrchr(lpParent, _T('/'));

                    if (lpNode)
                    {
                        ++lpNode;
                    }
                }
            }
        }

        int n1, n2;
        switch(fld)
        {
        case META_SERVICE:
            //
            // Requested the service string
            //
            if (lpSvc)
            {
                n1 = DIFF(lpSvc - lpMetaPath);
                n2 = lpInstance ? DIFF(lpInstance - lpSvc) : DIFF(lpEndPath - lpSvc);
                strField = strMetaPath.Mid(n1, n2 - 1);
            }
            break;

        case META_INSTANCE:
            //
            // Requested the instance number
            //
            if (lpInstance)
            {
                n1 = DIFF(lpInstance - lpMetaPath);
                n2 = lpParent ? DIFF(lpParent - lpInstance) : DIFF(lpEndPath - lpInstance);
                strField = strMetaPath.Mid(n1, n2 - 1);
            }

            break;

        case META_PARENT:
            //
            // Requestd the parent path
            //
            if (lpParent)
            {
                n1 = DIFF(lpParent - lpMetaPath);
                n2 = lpNode ? DIFF(lpNode - lpParent) : DIFF(lpEndPath - lpParent);
                strField = strMetaPath.Mid(n1, n2 - 1);
            }
            break;

        case META_NODE:
            //
            // Requested the node name
            //
            if (lpNode)
            {
                n1 = DIFF(lpNode - lpMetaPath);
                n2 = DIFF(lpEndPath - lpNode);
                strField = strMetaPath.Mid(n1, n2 - 1);
            }
            break;

        default:
            //
            // Bogus
            //
            ASSERT(FALSE);
            return E_FAIL;
        }
    }

    TRACEEOLID("Requested metabase path data: " << strField);
    int len = strField.GetLength() + 1;

    return Create((LPCTSTR)strField, (len) * sizeof(TCHAR), lpMedium);
}



HRESULT
CDataObject::CreateCoClassID(
    IN LPSTGMEDIUM lpMedium
    )
/*++

Routine Description:

    Create the CoClassID

Arguments:

    LPSTGMEDIUM lpMedium        : Medium

Return Value:

    HRESULT

--*/
{
    //
    // Create the CoClass information
    //
    return Create((const void *)&m_internal.m_clsid, sizeof(CLSID), lpMedium);
}